home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 676-700 / 697 / hackdisk / hackdisk.s < prev    next >
Text File  |  1995-03-18  |  67KB  |  2,972 lines

  1. ********************************************************************************
  2. * hackdisk.s -- my very own version of trackdisk.device
  3. * Copyright (C) 1992 by Dan Babcock
  4. *
  5. * Version history:
  6. * V1.00 04/25/92 First version. Supports 880K drives only.
  7. * V1.01 04/28/92 Optimized CopyMem
  8. * V1.02 04/30/92 More intelligent write algorithm fixes performance problem
  9. * V1.03 05/03/92 Sets IO_ACTUAL
  10. * V1.04 06/24/92 Fixes a couple bugs: RemChangeInt works now, and disk change
  11. *                errors are only reported when it makes sense.
  12. *                CrossDOSV5/CrossPC flushed out these bugs.
  13. * V1.10 07/04/92 Compatible with Kickstart 1.2 and higher
  14. *                Fixed Seek (IO_OFFSET is in bytes, not tracks)
  15. ********************************************************************************
  16.  
  17. ;Standard register usage:
  18. ;A6            - ExecBase or $dff000
  19. ;A5 (A_DEVICE) - device ptr
  20. ;A4 (A_UNIT)   - unit ptr
  21. ;A3 (A_IO)     - IORequest
  22.  
  23.     exeobj
  24.     multipass
  25.  
  26. ;There are supposed to be two modes of operation, the RamKick (ROM) mode
  27. ;and the DEVS:/mountlist (DISK) mode. However, the disk mode is only
  28. ;half-implemented, because I decided at some point it was useless.
  29. ROM    equ    1
  30. DISK    equ    2    ;NOTE: Not currently functioning!
  31. TYPE    equ    ROM
  32.  
  33. ;Set INFO_LEVEL to 1 for full debugging output over the internal serial port.
  34. INFO_LEVEL    equ    0
  35.  
  36.     objfile    'devs:hackdisk.device'
  37.  
  38. A_DEVICE    equr    a5
  39. A_UNIT    equr    a4
  40. A_IO    equr    a3
  41.  
  42.  
  43. VERSION    equ    127
  44. REVISION    equ    10
  45. MYPRI    equ    0
  46.  
  47. MD_NUMUNITS    equ    4
  48. STACKSIZE    equ    4000
  49. TASKPRI    equ    5
  50.  
  51. RETRYCNT    equ    3
  52.  
  53. ;Put a message to the serial port.  Used like so:
  54. ;
  55. ;PUTDEBUG    <'Init: called'>
  56. ;
  57. ;Parameters can be printed out by pushing them on the stack and
  58. ;adding the appropriate C printf-style % formatting commands.
  59.  
  60. PUTDEBUG     macro    ;<msg>
  61.     ifne    INFO_LEVEL
  62.     movem.l    d0-d1/a0-a1,-(sp)
  63.     lea    .msg\@(pc),a0    ;Point to static format string
  64.     lea    16(sp),a1    ;Point to args
  65.     bsr    KPutFmt
  66.     movem.l    (sp)+,d0-d1/a0-a1
  67.     bra    .end\@
  68. .msg\@:
  69.     dc.b    \1,$a,0
  70.     even
  71. .end\@:
  72.     endc
  73.     endm
  74.  
  75. *--------------------------------------------------------------------
  76. *
  77. * Driver error defines
  78. *
  79. *--------------------------------------------------------------------
  80.  
  81. ;TDERR_NotSpecified    EQU    20    ; general catchall
  82. ;TDERR_NoSecHdr        EQU    21    ; couldn't even find a sector
  83. ;TDERR_BadSecPreamble    EQU    22    ; sector looked wrong
  84. ;TDERR_BadSecID        EQU    23    ; ditto
  85. ;TDERR_BadHdrSum        EQU    24    ; header had incorrect checksum
  86. ;TDERR_BadSecSum        EQU    25    ; data had incorrect checksum
  87. ;TDERR_TooFewSecs    EQU    26    ; couldn't find enough sectors
  88. ;TDERR_BadSecHdr        EQU    27    ; another "sector looked wrong"
  89. ;TDERR_WriteProt        EQU    28    ; can't write to a protected disk
  90. ;TDERR_DiskChanged    EQU    29    ; no disk in the drive
  91. ;TDERR_SeekError        EQU    30    ; couldn't find track 0
  92. ;TDERR_NoMem        EQU    31    ; ran out of memory
  93. ;TDERR_BadUnitNum    EQU    32    ; asked for a unit > NUMUNITS
  94. ;TDERR_BadDriveType    EQU    33    ; not a drive that trackdisk groks
  95. ;TDERR_DriveInUse    EQU    34    ; someone else allocated the drive
  96. ;TDERR_PostReset        EQU    35    ; user hit reset; awaiting doom
  97.  
  98. ; STRUCTURE TIMEVAL,0
  99. ;    ULONG    TV_SECS
  100. ;    ULONG    TV_MICRO
  101. ;    LABEL    TV_SIZE
  102.  
  103. ;Unit structure
  104.  
  105. *--------------------------------------------------------------------
  106. *
  107. * Public portion of unit structure
  108. *
  109. *--------------------------------------------------------------------
  110.  
  111. ;*------ UNIT_FLAG definitions:
  112.  
  113. ;These are bogus, but I won't re-define them.
  114. ;    BITDEF  UNIT,ACTIVE,0        ; driver is active
  115. ;    BITDEF  UNIT,INTASK,1        ; running in driver's task
  116.     BITDEF    UNIT,DiskInDrive,2
  117.     BITDEF    UNIT,WriteProtected,3
  118.  
  119. ; STRUCTURE TDU_PUBLICUNIT,UNIT_SIZE
  120. ;    UWORD    TDU_COMP01TRACK        ; track for first precomp
  121. ;    UWORD    TDU_COMP10TRACK        ; track for second precomp
  122. ;    UWORD    TDU_COMP11TRACK        ; track for third precomp
  123. ;    ULONG    TDU_STEPDELAY        ; time to wait after stepping
  124. ;    ULONG    TDU_SETTLEDELAY        ; time to wait after seeking
  125. ;    UBYTE    TDU_RETRYCNT        ; # of times to retry
  126. ;    UBYTE    TDU_PUBFLAGS        ; public flags, see below
  127. ;    UWORD    TDU_CURRTRK        ; track heads are over
  128.                     ;  (ONLY ACCESS WHILE UNIT IS STOPPED!)
  129. ;    ULONG    TDU_CALIBRATEDELAY    ; time to wait after stepping
  130.                     ; for recalibrate
  131. ;    ULONG    TDU_COUNTER        ; counter for disk changes
  132.                     ;  (ONLY ACCESS WHILE UNIT IS STOPPED!)
  133. ;    LABEL    TDU_PUBLICUNITSIZE
  134. ;*--------------------------------------------------------------------
  135.  
  136.     STRUCTURE    MyUnit,TDU_PUBLICUNITSIZE
  137.     STRUCT    Drive_LastCheck,TV_SIZE    ;last time diskchange was checked
  138.     STRUCT    ChangeIntList,MLH_SIZE    ;must be initialized!!!
  139.     LONG    TDRemoveInt
  140.  
  141.     IFEQ    TYPE-DISK
  142. ;not used by ROM driver
  143.     STRUCT    ChangeInt,IS_SIZE
  144.     STRUCT    TDPort,MP_SIZE
  145.     STRUCT    TDIORequest,IOTD_SIZE
  146.     ENDC
  147.  
  148.     BYTE    UnitNum
  149.     BYTE    MyUnit_Pad
  150. ;MUST be word-aligned!!!
  151.     LABEL    MyUnit_Sizeof
  152.  
  153. ;*
  154. ;* Flags for TDU_PUBFLAGS:
  155. ;*
  156. ;    BITDEF    TDP,NOCLICK,0        ; set to enable noclickstart
  157.     BITDEF    TDP,VERIFY,1
  158.  
  159. ;Device global structure -- accessed as positive offsets from device base
  160. ;Note: The stuff in LIB_SIZE is *required*. Anything else is up to you...
  161.     STRUCTURE    DeviceGlobals,LIB_SIZE
  162.     STRUCT    TaskPort,MP_SIZE    ;MsgPort for task
  163.     STRUCT    Task,TC_SIZE
  164.     STRUCT    TaskStack,STACKSIZE
  165.     STRUCT    TempTimeVal,TV_SIZE
  166.     LONG    SegList    ;not used for ROM version
  167.     STRUCT    TimerIORequest,IOTV_SIZE
  168.     STRUCT    TimerPort,MP_SIZE
  169.     STRUCT    LastCheck,TV_SIZE    ;last time diskchange was checked
  170.     STRUCT    DiskResourceUnit,DRU_SIZE    ;must be initialized!!!
  171.     STRUCT    DiskResourcePort,MP_SIZE    ;must be initialized!!!
  172.  
  173. ;Allocated memory pointers
  174.     LONG    RawBuffer
  175.     LONG    DecodedBuffer
  176.     LONG    VerifyBuffer
  177.  
  178.     STRUCT    SectorLabels,11*16
  179.     STRUCT    Unit0,MyUnit_Sizeof
  180.     STRUCT    Unit1,MyUnit_Sizeof
  181.     STRUCT    Unit2,MyUnit_Sizeof
  182.     STRUCT    Unit3,MyUnit_Sizeof
  183.     LONG    GraphBase
  184.     LONG    IntBase
  185.     LONG    DiskResourceBase
  186.     LONG    CIABase
  187.     WORD    SyncCount
  188.     WORD    IndexDskLen
  189.     LONG    WriteMap
  190.  
  191.     LABEL    StartSigs
  192.     LONG    SyncSig
  193.     LONG    BlockSig
  194.     LABEL    EndSigs
  195.  
  196.     BYTE    BufferTrack
  197.     BYTE    BufferDrive
  198.     BYTE    DEV_FLAGS
  199.     BYTE    MotorState
  200.     BYTE    InquireBits
  201.     LABEL    MyDev_Sizeof
  202.  
  203. NumSigs    equ    (EndSigs-StartSigs)/4
  204.  
  205. ;Bit definitions for DEV_FLAGS:
  206.     BITDEF    DEV,Dirty,0    ;buffered track has been changed
  207.     BITDEF    DEV,Stopped,1    ;device is stopped
  208.     BITDEF    DEV,Verify,2    ;used by BlockInt for verify
  209.  
  210. ;    BITDEF    DEV,Test,3    ;for debugging only
  211.  
  212.  
  213.     IFEQ    TYPE-DISK
  214. ;The first executable location.  This should return an error in case someone
  215. ;tried to run us as a program (instead of loading us as a device).
  216.     moveq    #-1,d0
  217.     rts
  218.     ENDC
  219.  
  220. ;A romtag structure.  After your driver is brought in from disk, the
  221. ;disk image will be scanned for this structure to discover magic constants
  222. ;about you (such as where to start running you from...).
  223.  
  224. RomTag:
  225.     dc.w    RTC_MATCHWORD    ;$4AFC ('illegal' opcode)
  226.     dc.l    RomTag
  227.     dc.l    EndCode    ;pointer to end of code
  228.     dc.b    RTF_AUTOINIT+RTF_COLDSTART    ;set things up automatically
  229.     dc.b    VERSION
  230.     dc.b    NT_DEVICE    ;module type (either device or library)
  231.     dc.b    MYPRI    ;usually not important
  232.     dc.l    Name    ;name used in OpenDevice
  233.     dc.l    IDString    ;optional
  234.     dc.l    Init    ;more init info
  235.  
  236.     IFEQ    TYPE-ROM
  237. Name:    dc.b    'trackdisk.device',0
  238.     even
  239.     ENDC
  240.     IFEQ    TYPE-DISK
  241. Name:    dc.b    'hackdisk.device',0        ;this MUST be the same as the
  242.     even                ;disk-resident name!
  243.     ENDC
  244.  
  245. IDString:    dc.b    'Hackdisk V1.10 - Copyright (C) 1992 by Dan Babcock',$a,0
  246.     even
  247. DiskResourceName:
  248.     dc.b    'disk.resource',0
  249. TimerName:
  250.     dc.b    'timer.device',0
  251. GraphName:
  252.     dc.b    'graphics.library',0
  253. CIAName:    dc.b    'ciab.resource',0
  254. IntName:    dc.b    'intuition.library',0
  255.     IFEQ    TYPE-DISK
  256. TDName:    dc.b    'trackdisk.device',0
  257.     ENDC
  258.     even
  259.  
  260. ;The romtag specified that we were "RTF_AUTOINIT".  This means that the
  261. ;RT_INIT structure member points to one of these tables below.  If the
  262. ;AUTOINIT bit was not set then RT_INIT would point to a routine to run.
  263.  
  264. Init:    dc.l    MyDev_Sizeof    ;data space size (at least
  265.                 ;LIB_SIZE!!)
  266.     dc.l    FuncTable    ;pointer to function initializers
  267.     dc.l    DataTable    ;pointer to data initializers
  268.     dc.l    InitRoutine    ;routine to run
  269.  
  270. FuncTable:
  271.     dc.w    -1    ;this indicates that the following are offsets,
  272.             ;rather than absolute addresses
  273.     dc.w    Open-FuncTable    ;standard system routines
  274.     dc.w    _Close-FuncTable
  275.     dc.w    Expunge-FuncTable
  276.     dc.w    .Null-FuncTable    ;Reserved for future use!
  277.     dc.w    BeginIO-FuncTable    ;device definitions
  278.     dc.w    AbortIO-FuncTable
  279.     dc.w    -1    ;function table end marker
  280. .Null:    moveq    #0,d0
  281.     rts
  282.  
  283. ;The data table initializes static data structures. The format is
  284. ;specified in exec/InitStruct routine's manual pages.  The
  285. ;INITBYTE/INITWORD/INITLONG macros are in the file "exec/initializers.i".
  286. ;The first argument is the offset from the device base for this
  287. ;byte/word/long. The second argument is the value to put in that cell.
  288. ;The table is null terminated
  289.  
  290. DataTable:
  291.     INITBYTE    LN_TYPE,NT_DEVICE
  292.     INITLONG    LN_NAME,Name
  293.     INITBYTE    LIB_FLAGS,LIBF_SUMUSED+LIBF_CHANGED
  294.     INITWORD    LIB_VERSION,VERSION
  295.     INITWORD    LIB_REVISION,REVISION
  296.     INITLONG    LIB_IDSTRING,IDString
  297.     dc.w    0    ;terminate list (only one byte needed)
  298.  
  299. CmdTable:    dc.w    Invalid-CmdTable        ;0    CMD_INVALID
  300.     dc.w    Invalid-CmdTable        ;1    CMD_RESET
  301.     dc.w    Read-CmdTable        ;2    CMD_READ
  302.     dc.w    Write-CmdTable        ;3    CMD_WRITE / ETD_
  303.     dc.w    Update-CmdTable        ;4    CMD_UPDATE / ETD_
  304.     dc.w    Clear-CmdTable        ;5    CMD_CLEAR / ETD_
  305.     dc.w    MyStop-CmdTable        ;6    CMD_STOP / ETD_
  306.     dc.w    Start-CmdTable        ;7    CMD_START
  307.     dc.w    Invalid-CmdTable        ;8    CMD_FLUSH
  308.     dc.w    Motor-CmdTable        ;9    TD_MOTOR / ETD_
  309.     dc.w    TDSeek-CmdTable        ;10    TD_SEEK / ETD_
  310.     dc.w    Format-CmdTable        ;11    TD_FORMAT
  311.     dc.w    TDRemove-CmdTable        ;12    TD_REMOVE
  312.     dc.w    ChangeNum-CmdTable        ;13    TD_CHANGENUM
  313.     dc.w    ChangeState-CmdTable    ;14    TD_CHANGESTATE
  314.     dc.w    ProtStatus-CmdTable        ;15    TD_PROTSTATUS
  315.     dc.w    TDRawRead-CmdTable        ;16    TD_RAWREAD
  316.     dc.w    RawWrite-CmdTable        ;17    TD_RAWWRITE
  317.     dc.w    GetDriveType-CmdTable    ;18    TD_GETDRIVETYPE
  318.     dc.w    GetNumTracks-CmdTable    ;19    TD_GETNUMTRACKS
  319.     dc.w    AddChangeInt-CmdTable    ;20    TD_ADDCHANGEINT
  320.     dc.w    RemChangeInt-CmdTable    ;21    TD_REMCHANGEINT
  321.     dc.w    GetGeometry-CmdTable    ;22    TD_GETGEOMETRY
  322.     dc.w    Invalid-CmdTable        ;23    TD_EJECT
  323. EndCmdTable:
  324. HighestCommand    equ    ((EndCmdTable-CmdTable)/2)-1
  325. Invalid:    move.b    #IOERR_NOCMD,IO_ERROR(A_IO)
  326.     rts
  327.  
  328. InitRoutine:
  329. ;A0 - segment
  330. ;D0 - device ptr
  331.  
  332. ;Returns with the device ptr still in D0 if successful, otherwise zero.
  333.  
  334.     PUTDEBUG    <'InitRoutine: Entered'>
  335.     movem.l    d1-d7/a0-a6,-(sp)
  336.     move.l    d0,A_DEVICE
  337.     move.l    a0,SegList(A_DEVICE)
  338.     move.l    4,a6
  339.  
  340. ;Initialize ports
  341.     lea    TaskPort(A_DEVICE),a0
  342.     bsr    InitPort
  343.     lea    TimerPort(A_DEVICE),a0
  344.     bsr    InitPort
  345.  
  346. ;Invalidate track buffer
  347.     bsr    Clear
  348.  
  349. ;Open libraries/resources
  350.  
  351.     lea    DiskResourceName(pc),a1    ;'disk.resource'
  352.     SYS    OpenResource
  353.     move.l    d0,DiskResourceBase(A_DEVICE)
  354.     beq    .Failed
  355.     lea    CIAName(pc),a1    ;'ciab.resource'
  356.     SYS    OpenResource
  357.     move.l    d0,CIABase(A_DEVICE)
  358.     beq    .Failed
  359.  
  360.     lea    GraphName(pc),a1
  361.     SYS    OldOpenLibrary
  362.     move.l    d0,GraphBase(A_DEVICE)
  363.     beq    .Failed
  364.     lea    IntName(pc),a1
  365.     SYS    OldOpenLibrary
  366.     move.l    d0,IntBase(A_DEVICE)
  367.     beq    .Failed
  368.  
  369. ;Allocate CHIP memory. (Note: This will change when I support other drive
  370. ;types).
  371.     move.l    #DISK_RawBufSize,d0
  372.     move.l    #MEMF_CHIP,d1
  373.     SYS    AllocMem
  374.     move.l    d0,RawBuffer(A_DEVICE)
  375.     beq    .Failed
  376.  
  377.     move.l    #512*11,d0
  378.     move.l    #MEMF_CHIP,d1
  379.     SYS    AllocMem
  380.     move.l    d0,DecodedBuffer(A_DEVICE)
  381.     beq    .Failed
  382.  
  383.     move.l    #(1088*11)+2,d0
  384.     move.l    #MEMF_CHIP,d1
  385.     SYS    AllocMem
  386.     move.l    d0,VerifyBuffer(A_DEVICE)
  387.     beq    .Failed
  388.  
  389. ;Set up disk.resource structure.
  390.     lea    DiskResourcePort(A_DEVICE),a0
  391.     bsr    InitPort
  392.     move.l    a0,DiskResourceUnit+MN_REPLYPORT(A_DEVICE)
  393. ;    move.b    #PA_SIGNAL,MP_FLAGS(a0)    ;not needed (0)
  394.     lea    Name(pc),a0
  395.     move.l    a0,DiskResourceUnit+LN_NAME(A_DEVICE)
  396.  
  397.     move.l    A_DEVICE,DiskResourceUnit+DRU_DISCBLOCK+IS_DATA(A_DEVICE)
  398.     move.l    A_DEVICE,DiskResourceUnit+DRU_DISCSYNC+IS_DATA(A_DEVICE)
  399.     move.l    A_DEVICE,DiskResourceUnit+DRU_INDEX+IS_DATA(A_DEVICE)
  400.     lea    BlockInt(pc),a0
  401.     move.l    a0,DiskResourceUnit+DRU_DISCBLOCK+IS_CODE(A_DEVICE)
  402.     lea    SyncInt(pc),a0
  403.     move.l    a0,DiskResourceUnit+DRU_DISCSYNC+IS_CODE(A_DEVICE)
  404.     lea    IndexInt(pc),a0
  405.     move.l    a0,DiskResourceUnit+DRU_INDEX+IS_CODE(A_DEVICE)
  406.     moveq    #NT_INTERRUPT,d0
  407.     move.b    d0,DiskResourceUnit+DRU_DISCBLOCK+LN_TYPE(A_DEVICE)
  408.     move.b    d0,DiskResourceUnit+DRU_DISCSYNC+LN_TYPE(A_DEVICE)
  409.     move.b    d0,DiskResourceUnit+DRU_INDEX+LN_TYPE(A_DEVICE)
  410.  
  411. ;Initialize timer IORequest (except signal)
  412.     lea    TimerPort(A_DEVICE),a0
  413. ;    move.b    #PA_SIGNAL,MP_FLAGS(a0)    ;not needed (0)
  414.     move.l    a0,TimerIORequest+MN_REPLYPORT(A_DEVICE)
  415.     lea    TimerName(pc),a0
  416.     moveq    #UNIT_MICROHZ,d0
  417.     lea    TimerIORequest(A_DEVICE),a1
  418.     moveq    #0,d1
  419.     SYS    OpenDevice
  420.     tst.l    d0
  421.     bne    .Failed
  422.  
  423. ;*** Set up unit structures
  424.     moveq    #0,d2
  425.     lea    Unit0(A_DEVICE),A_UNIT
  426. .UnitLoop:
  427.  
  428.     IFEQ    TYPE-DISK
  429.     lea    TDPort(A_UNIT),a0
  430.     bsr    InitPort
  431.     move.l    a0,TDIORequest+MN_REPLYPORT(A_UNIT)
  432.     ENDC
  433.  
  434.     move.b    d2,UnitNum(A_UNIT)
  435.     lea    ChangeIntList(A_UNIT),a0
  436.     NEWLIST    a0
  437.  
  438.     bset    #1,TDU_PUBFLAGS(A_UNIT)    ;verify ON!
  439.     moveq    #-1,d0
  440.     move.w    #80,TDU_COMP01TRACK(A_UNIT)
  441.     move.w    d0,TDU_COMP10TRACK(A_UNIT)
  442.     move.w    d0,TDU_COMP11TRACK(A_UNIT)
  443.     move.l    #3*1000,TDU_STEPDELAY(A_UNIT)
  444.     move.l    #15*1000,TDU_SETTLEDELAY(A_UNIT)
  445.     move.b    #RETRYCNT,TDU_RETRYCNT(A_UNIT)
  446.     move.l    #4*1000,TDU_CALIBRATEDELAY(A_UNIT)
  447.  
  448.     lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  449.     addq.b    #1,d2
  450.     cmp.b    #MD_NUMUNITS,d2
  451.     blo    .UnitLoop
  452.  
  453. ;Set up task
  454.     move.b    #PA_IGNORE,TaskPort+MP_FLAGS(A_DEVICE)
  455.     move.b    #NT_MSGPORT,TaskPort+LN_TYPE(A_DEVICE)
  456.     lea    Task(A_DEVICE),a1    ;Task Control Block
  457.     lea    Name(pc),a0
  458.     move.l    a0,LN_NAME(a1)
  459.     move.b    #TASKPRI,LN_PRI(a1)
  460.     move.b    #NT_TASK,LN_TYPE(a1)
  461.     lea    TaskCode(pc),a2    ;initial PC
  462.     sub.l    a3,a3    ;final PC
  463.     lea    TaskStack(A_DEVICE),a0
  464.     move.l    a0,TC_SPLOWER(a1)
  465.     lea    STACKSIZE(a0),a0
  466.     move.l    a0,TC_SPUPPER(a1)
  467.  
  468.     move.l    A_DEVICE,-(a0)    ;pass device pointer to task
  469.     move.l    a0,TC_SPREG(a1)
  470.     move.l    a1,TaskPort+MP_SIGTASK(A_DEVICE)
  471.     SYS    AddTask
  472.  
  473.     move.l    A_DEVICE,d0
  474.     PUTDEBUG    <'InitRoutine: Done'>
  475. .End:    movem.l    (sp)+,d1-d7/a0-a6
  476.     rts
  477. .Failed:
  478.     PUTDEBUG    <'InitRoutine: Failed'>
  479.     bsr    CloseEverything
  480.     moveq    #0,d0
  481.     bra    .End
  482.  
  483. InitPort:
  484. ;Enter with pointer to port in A0. All registers preserved.
  485.     push    a0
  486.     lea    MP_MSGLIST(a0),a0
  487.     NEWLIST    a0
  488.     pop    a0
  489.     rts
  490.  
  491. CloseEverything:
  492. ;This routines cleans up device stuff. Enter with A_DEVICE.
  493.  
  494.     movem.l    d0-d1/a0-a1/a6,-(sp)
  495.     move.l    4,a6
  496. ;Close timer
  497.     tst.b    TimerIORequest+IO_ERROR(A_DEVICE)
  498.     beq    .SkipTimer
  499.     lea    TimerIORequest(A_DEVICE),a1
  500.     SYS    CloseDevice
  501. .SkipTimer:
  502.  
  503. ;Remove task
  504.     lea    Name(pc),a1
  505.     SYS    FindTask
  506.     tst.l    d0
  507.     beq    .SkipTask
  508.     move.l    d0,a1
  509.     SYS    RemTask
  510. .SkipTask:
  511.  
  512. ;Close libraries
  513.     move.l    GraphBase(A_DEVICE),d0
  514.     beq    .SkipGfx
  515.     move.l    d0,a1
  516.     SYS    CloseLibrary
  517. .SkipGfx:
  518.     move.l    IntBase(A_DEVICE),d0
  519.     beq    .SkipInt
  520.     move.l    d0,a1
  521.     SYS    CloseLibrary
  522. .SkipInt:
  523.  
  524. ;Free CHIP RAM. (Note: This will change when I support other drive types).
  525.     move.l    RawBuffer(A_DEVICE),d0
  526.     beq    .SkipRaw
  527.     move.l    d0,a1
  528.     move.l    #DISK_RawBufSize,d0
  529.     SYS    FreeMem
  530. .SkipRaw:
  531.     move.l    DecodedBuffer(A_DEVICE),d0
  532.     beq    .SkipDecoded
  533.     move.l    d0,a1
  534.     move.l    #512*11,d0
  535.     SYS    FreeMem
  536. .SkipDecoded:
  537.     move.l    VerifyBuffer(A_DEVICE),d0
  538.     beq    .SkipVerify
  539.     move.l    d0,a1
  540.     move.l    #(1088*11)+2,d0
  541.     SYS    FreeMem
  542. .SkipVerify:
  543. .End:    movem.l    (sp)+,d0-d1/a0-a1/a6
  544.     rts
  545.  
  546. ;******************************* Task code ******************************
  547.  
  548. TaskCode:
  549.     PUTDEBUG    <'Task: Entered'>
  550.     move.l    4,a6
  551.  
  552. ;Grab the arguments passed down from our parent
  553.     move.l    4(sp),A_DEVICE    ;Device pointer
  554.  
  555. ;General initialization
  556.  
  557.     lea    StartSigs(A_DEVICE),a2
  558.     moveq    #NumSigs-1,d2    ;Number of signals to allocate-1
  559. .SigLoop:
  560.     moveq    #-1,d0    ;-1 is any signal at all
  561.     SYS    AllocSignal    ;Allocate signals for I/O interrupts
  562.     moveq    #0,d1    ;Convert bit number signal mask
  563.     bset    d0,d1
  564.     move.l    d1,(a2)+    ;Save in unit structure
  565.     dbra    d2,.SigLoop
  566.  
  567.     moveq    #-1,d0    ;-1 is any signal at all
  568.     SYS    AllocSignal    ;Allocate a signal
  569.     move.b    d0,TaskPort+MP_SIGBIT(A_DEVICE)
  570.     move.b    #PA_SIGNAL,TaskPort+MP_FLAGS(A_DEVICE)    ;Make message port "live"
  571.  
  572.     moveq    #-1,d0    ;-1 is any signal at all
  573.     SYS    AllocSignal    ;Allocate a signal
  574.     move.b    d0,TimerPort+MP_SIGBIT(A_DEVICE)
  575.     move.l    ThisTask(a6),TimerPort+MP_SIGTASK(A_DEVICE)
  576.  
  577.     moveq    #-1,d0    ;-1 is any signal at all
  578.     SYS    AllocSignal    ;Allocate a signal
  579.     move.b    d0,DiskResourcePort+MP_SIGBIT(A_DEVICE)
  580.     move.l    ThisTask(a6),DiskResourcePort+MP_SIGTASK(A_DEVICE)
  581.  
  582.     bsr    GetDrive
  583.     bsr    Inquire
  584.  
  585. ;Disk version only:
  586. ;Open trackdisk.device for all valid units and install diskchange handler.
  587.     IFEQ    TYPE-DISK
  588.     moveq    #MD_NUMUNITS-1,d2
  589.     move.b    InquireBits(A_DEVICE),d3
  590.     lea    Unit0(A_DEVICE),A_UNIT
  591. .TDLoop:
  592.     moveq    #-1,d0    ;-1 is any signal at all
  593.     SYS    AllocSignal    ;Allocate a signal
  594.     move.b    d0,TDPort+MP_SIGBIT(A_UNIT)
  595.     move.l    ThisTask(a6),TDPort+MP_SIGTASK(A_UNIT)
  596.     moveq    #0,d0
  597.     move.b    UnitNum(A_UNIT),d0
  598.     btst    d0,d3
  599.     beq    .NextTD
  600.     moveq    #0,d1
  601.     lea    TDName(pc),a0
  602.     lea    TDIORequest(A_DEVICE),a1
  603.     SYS    OpenDevice
  604.     tst.l    d0
  605.     beq    .TDOK
  606. ;Should never get here
  607. ..    move.w    #$0f00,$dff180
  608.     bra    ..
  609. .TDOK:
  610.  
  611. ;Install a diskchange handler via TD_ADDCHANGEINT.
  612.     lea    TDIORequest(A_DEVICE),a1
  613.     lea    ChangeInt(A_UNIT),a0
  614.     lea    ChangeIntCode(pc),a2
  615.     move.l    a2,IS_CODE(a0)
  616.     move.l    A_UNIT,IS_DATA(a0)
  617.     move.l    a0,IO_DATA(a1)
  618.     move.l    #IS_SIZE,IO_LENGTH(a1)    ;this is dumb
  619.     move.w    #TD_ADDCHANGEINT,IO_COMMAND(a1)
  620.     SYS    SendIO
  621.  
  622. .NextTD:    lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  623.     dbra    d2,.TDLoop
  624.     ENDC
  625.  
  626.     IFNE    INFO_LEVEL
  627.     push    d0
  628.     moveq    #0,d0
  629.     move.b    InquireBits(A_DEVICE),d0
  630.     move.l    d0,-(sp)
  631.     PUTDEBUG    <'InquireBits=%lx'>
  632.     addq.l    #4,sp
  633.     pop    d0
  634.     ENDC
  635.  
  636.     bsr    SeekZeroAll    ;Send all drives to track zero.
  637.     bsr    FreeDrive
  638.  
  639.     bra    .NextMessage
  640.  
  641. ;Main loop: Wait for a new message and handle disk changes.
  642.  
  643. .MainLoop:
  644.  
  645. ;    PUTDEBUG    <'Task: Waiting'>
  646.     moveq    #0,d0
  647.     move.b    MP_SIGBIT+TaskPort(A_DEVICE),d1
  648.     bset    d1,d0
  649.     move.l    #500000,d1
  650.     bsr    TimeOutWait
  651.     tst.l    d0
  652.     bne    .NextMessage
  653.  
  654. ;0.5 seconds have passed without receiving any work, so we check for disk
  655. ;changes, then drop into .NextMessage.
  656. ;Note: A6 undefined.
  657.  
  658. .CheckDiskChange:
  659.     moveq    #MD_NUMUNITS-1,d2
  660.     move.b    InquireBits(A_DEVICE),d3
  661.     lea    Unit0(A_DEVICE),A_UNIT
  662.     bsr    GetDrive
  663.     move.l    TimerIORequest+IO_DEVICE(A_DEVICE),a6
  664.  
  665. ;Algorithm (for each unit):
  666. ;Check hardware diskchange bit. If disk was ejected, seek to track zero,
  667. ;clear DiskInDrive bit. End.
  668. ;If no disk in drive, check NoClick bit. If set, immediately step toward
  669. ;track zero (using custom step routine). If clear, check the
  670. ;Drive_LastChecked time against the current time. If less than 2.5 seconds,
  671. ;End. If >2.5 seconds, step the head toward track zero, unless already on
  672. ;track zero. Check the hardware diskchange bit. If a disk is present in the
  673. ;drive set the DiskInDrive bit and check the hardware write protect status,
  674. ;setting WriteProtected as needed. End.
  675. ;Note: We also increment the diskchange counter in the public part of the
  676. ;unit structure, if a disk was inserted or removed.
  677.  
  678. .UnitLoop:
  679.     move.b    UnitNum(A_UNIT),d4
  680.     btst    d4,d3
  681.     beq    .NoCheck
  682.  
  683.     lea    TempTimeVal(A_DEVICE),a0
  684.     bsr    GetSysTime
  685.  
  686.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  687.     beq    .ThinkNoDisk
  688.     bsr    SelectDriveSameMotor
  689.     btst    #CIAB_DSKCHANGE,ciaapra
  690.     bne    .NoChange
  691. ;Disk was removed.
  692.     bclr    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  693.     bsr    SeekZero
  694.     bsr    MotorOff
  695.     bsr    Clear
  696.     bra    .Change
  697. .ThinkNoDisk:
  698.     btst    #TDPB_NOCLICK,TDU_PUBFLAGS(A_UNIT)
  699.     beq    .Click
  700.     bsr    SelectDriveSameMotor
  701.     bset    #CIAB_DSKDIREC,ciabprb    ;set to "out" (lower tracks)
  702.     bset    #CIAB_DSKSTEP,ciabprb
  703.     bclr    #CIAB_DSKSTEP,ciabprb    ;step head
  704.     bset    #CIAB_DSKSTEP,ciabprb
  705.     bsr    Deselect
  706.     move.l    TDU_SETTLEDELAY(A_UNIT),d0
  707.     bsr    delay
  708.     bsr    SelectDriveSameMotor
  709.     bra    .ChkChg
  710. .Click:
  711.     lea    Drive_LastCheck(A_UNIT),a1
  712.     SYS    SubTime
  713.     cmp.l    #2,TV_SECS(a0)
  714.     blo    .NoCheck
  715.     bhi    .SkipMicro
  716.     cmp.l    #500000,TV_MICRO(a0)
  717.     blo    .NoCheck
  718. .SkipMicro:
  719.     lea    Drive_LastCheck(A_UNIT),a0
  720.     bsr    GetSysTime
  721.     move.b    UnitNum(A_UNIT),d2    ;for Seek
  722.     move.w    TDU_CURRTRK(A_UNIT),d3
  723.     subq.w    #2,d3
  724.     bpl    .NotZero
  725.     moveq    #2,d3
  726. .NotZero:    bsr    SelectDriveSameMotor
  727.     bsr    Seek
  728. .ChkChg:    btst    #CIAB_DSKCHANGE,ciaapra
  729.     beq    .NoChange    ;no disk in drive
  730.     bset    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  731.     bclr    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  732.     btst    #CIAB_DSKPROT,ciaapra
  733.     bne    .Change    ;not write protected
  734.     bset    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  735. .Change:
  736.     bsr    Deselect
  737.     addq.l    #1,TDU_COUNTER(A_UNIT)
  738.  
  739. ;This code notifies users of TD_REMOVE and TD_ADDCHANGEINT via Cause.
  740.  
  741.     push    a6
  742.     move.l    4,a6
  743.     SYS    Forbid
  744.     move.l    TDRemoveInt(A_UNIT),d0
  745.     beq    .NoRemoveInt
  746.     move.l    d0,a1
  747.     SYS    Cause
  748. .NoRemoveInt:
  749.     move.l    ChangeIntList+LH_HEAD(A_UNIT),a2
  750. .IntLoop:    move.l    (a2),d0
  751.     beq    .DoneInt
  752.     move.l    IO_DATA(a2),a1
  753.     move.l    d0,a2
  754.     SYS    Cause
  755.     bra    .IntLoop
  756. .DoneInt:    SYS    Permit
  757.     pop    a6
  758. .NoChange:
  759.     lea    LastCheck(A_DEVICE),a0
  760.     bsr    GetSysTime
  761. .NoCheck:
  762.     lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  763.     dbra    d2,.UnitLoop
  764.     move.l    4,a6
  765.     bsr    FreeDrive
  766.  
  767. .NextMessage:
  768.     btst    #DEVB_Stopped,DEV_FLAGS(A_DEVICE)    ;See if we are stopped
  769.     bne    .MainLoop    ;Device is stopped, ignore messages
  770.  
  771.     lea    TaskPort(A_DEVICE),a0
  772.     SYS    GetMsg    ;Get the next request
  773.     tst.l    d0
  774.     beq    .MainLoop    ;no message?
  775.     move.l    d0,A_IO    ;Do this request
  776.     move.l    IO_UNIT(A_IO),A_UNIT
  777.  
  778. ;Handle TDB_EXTCOM and dispatch command
  779.     move.w    IO_COMMAND(A_IO),d0
  780.  
  781.     IFNE    INFO_LEVEL
  782.     swap    d0
  783.     clr.w    d0
  784.     swap    d0
  785.     move.l    d0,-(sp)
  786.     PUTDEBUG    <'Command=%ld'>
  787.     addq.l    #4,sp
  788.     ENDC
  789.  
  790.     bclr    #TDB_EXTCOM,d0
  791.     beq    .NoExt
  792.     move.l    TDU_COUNTER(A_UNIT),d1
  793.     cmp.l    IOTD_COUNT(A_IO),d1
  794.     bls    .NoExt
  795. .ChgErr:    PUTDEBUG    <'Change error!'>
  796.     move.b    #TDERR_DiskChanged,IO_ERROR(A_IO)
  797.     bra    .Reply
  798. .NoExt:    move.l    #%000000111000111000011100,d1    ;specifies which commands should check for disk
  799.     btst    d0,d1
  800.     beq    .NoDiskNeeded
  801.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  802.     beq    .ChgErr
  803. .NoDiskNeeded:
  804.     bsr    GetDrive
  805.     lea    CmdTable(pc),a0
  806.     add.w    d0,d0
  807.     add.w    (a0,d0.w),a0
  808.     moveq    #0,d7    ;clear error flag
  809.     jsr    (a0)
  810.     bsr    FreeDrive
  811.     cmp.w    #TD_ADDCHANGEINT,IO_COMMAND(A_IO)
  812.     beq    .SkipReply
  813. .Reply:    move.l    A_IO,a1
  814.     SYS    ReplyMsg
  815. .SkipReply:
  816.  
  817. ;If at least 0.5 seconds has passed since last checking diskchange, do so
  818. ;now.
  819.     move.l    TimerIORequest+IO_DEVICE(A_DEVICE),a6
  820.     lea    TempTimeVal(A_DEVICE),a0
  821.     bsr    GetSysTime
  822.     lea    LastCheck(A_DEVICE),a1
  823.     SYS    SubTime
  824.     tst.l    TV_SECS(a0)
  825.     bne    .CheckDiskChange
  826.     cmp.l    #500000,TV_MICRO(a0)
  827.     bhs    .CheckDiskChange
  828.     lea    LastCheck(A_DEVICE),a0
  829.     bsr    GetSysTime
  830.     move.l    4,a6
  831.     bra    .NextMessage
  832.  
  833. GetSysTime:
  834.     cmp.w    #36,LIB_VERSION(a6)
  835.     bls    .OldKS
  836.     jmp    _LVOGetSysTime(a6)
  837. .OldKS:
  838. ;Compatibility routine for 1.2/1.3
  839.     movem.l    d0-d1/a0-a2/a6,-(sp)
  840.     move.l    a0,a2
  841.     lea    TimerIORequest(A_DEVICE),a1
  842.     move.l    4,a6
  843.     move.w    #TR_GETSYSTIME,IO_COMMAND(a1)
  844.     SYS    DoIO
  845.     lea    TimerIORequest(A_DEVICE),a1
  846.     move.l    IOTV_TIME+TV_SECS(a1),TV_SECS(a2)
  847.     move.l    IOTV_TIME+TV_MICRO(a1),TV_MICRO(a2)
  848.     movem.l    (sp)+,d0-d1/a0-a2/a6
  849.     rts
  850.  
  851.  
  852.     IFEQ    TYPE-DISK
  853. ChangeIntCode:
  854.     rts
  855.     ENDC
  856.  
  857. ;******************** External device routines ***********************
  858.  
  859. Open:
  860.  
  861. ;A6 - device ptr
  862. ;A1 - IORequest
  863. ;D0 - unit number
  864. ;D1 - flags (not used)
  865.  
  866.     PUTDEBUG    <'Open: Entered'>
  867.     moveq    #MD_NUMUNITS,d1
  868.     cmp.l    d1,d0
  869.     bhs    .Error
  870.     btst    d0,InquireBits(a6)
  871.     beq    .Error
  872.     lea    Unit0(a6),a0
  873.     mulu.w    #MyUnit_Sizeof,d0
  874.     add.l    d0,a0
  875.     move.l    a0,IO_UNIT(a1)
  876.     addq.w    #1,LIB_OPENCNT(a6)
  877.     clr.b    IO_ERROR(a1)    ;no error
  878.     move.b    #NT_REPLYMSG,LN_TYPE(a1)    ;Mark IORequest as "complete"
  879.     PUTDEBUG    <'Open: Done'>
  880.     rts
  881. .Error:
  882.     PUTDEBUG    <'Open: Failed'>
  883.  
  884. ;VirusX didn't like this.
  885. ;    move.b    #IOERR_OPENFAIL,IO_ERROR(a1)
  886.  
  887.     move.b    #TDERR_BadUnitNum,IO_ERROR(a1)
  888.     rts
  889.  
  890. _Close:
  891.  
  892. ;A6 - device ptr
  893. ;A1 - IORequest
  894.  
  895. ;Must return either 0 or, if the device wishes to be unloaded, the segment
  896. ;list.
  897.  
  898.     PUTDEBUG    <'Close: Called'>
  899.     IFEQ    TYPE-ROM
  900.     moveq    #0,d0
  901.     rts
  902.     ENDC
  903.  
  904.     IFEQ    TYPE-DISK
  905.     moveq    #0,d0
  906.     subq.w    #1,LIB_OPENCNT(a6)    ;Mark us as having one fewer openers
  907.     bne    .End    ;See if there is anyone left with us open
  908.     btst    #LIBB_DELEXP,LIB_FLAGS(a6)    ;See if we have a delayed expunge pending
  909.     beq    .End
  910.     bsr    Expunge    ;Expunge will return with SegList in D0
  911. .End:    rts
  912.     ENDC
  913.  
  914. Expunge:
  915.  
  916. ;A6 - device ptr
  917.  
  918. ;Must return either 0 or SegList ptr in D0.
  919.  
  920.     IFEQ    TYPE-ROM
  921.     moveq    #0,d0
  922.     rts
  923.     ENDC
  924.  
  925.     IFEQ    TYPE-DISK
  926.     movem.l    d2/a2/a6,-(sp)
  927.     tst.w    LIB_OPENCNT(a6)    ;See if anyone has us open
  928.     bne    .Delay
  929.     bsr    CloseEverything    ;clean up
  930.     move.l    SegList(a6),d2    ;Store our seglist in d2
  931.     move.l    a6,a1    ;Unlink from device list
  932.     move.l    a6,a2    ;save
  933.     move.l    4,a6
  934.     SYS    Remove     ;Remove first (before FreeMem)
  935.     move.l    a2,a1     ;device ptr
  936.     moveq    #0,d0
  937.     move.w    LIB_NEGSIZE(a2),d0
  938.     sub.l    d0,a1    ;Calculate base of functions
  939.     add.w    LIB_POSSIZE(a2),d0    ;Calculate size of functions + data area
  940.     SYS    FreeMem
  941.     move.l    d2,d0    ;Set up our return value
  942. .End:    movem.l    (sp)+,d2/a2/a6
  943.     rts
  944. .Delay:    bset    #LIBB_DELEXP,LIB_FLAGS(a6)    ;Set the delayed expunge flag
  945.     moveq    #0,d0
  946.     bra    .End
  947.     ENDC
  948.  
  949. BeginIO:
  950. ;A1 - IORequest
  951. ;A6 - device ptr
  952.  
  953.     movem.l    d7/a3-a6,-(sp)
  954.     move.l    a1,A_IO
  955.     move.l    IO_UNIT(A_IO),A_UNIT
  956.     move.l    a6,A_DEVICE
  957.     move.l    4,a6
  958.  
  959.     clr.b    IO_ERROR(A_IO)
  960.     move.b    #NT_MESSAGE,LN_TYPE(A_IO)    ;So WaitIO() is guaranteed to work
  961.  
  962. ;Decide whether the command is immediate or queued
  963.     move.w    IO_COMMAND(A_IO),d1
  964.     bclr    #15,d1
  965.     cmp.w    #HighestCommand,d1
  966.     bhi    .BadCmd
  967.     move.l    #%111011001111000111000011,d0    ;specifies what commands are immediate
  968.     btst    d1,d0
  969.     beq    .Queue
  970.     add.w    d1,d1
  971.     lea    CmdTable(pc),a0
  972.     add.w    (a0,d1.w),a0
  973.     moveq    #0,d7    ;clear error flag
  974.     jsr    (a0)
  975. .Reply:    btst    #IOB_QUICK,IO_FLAGS(A_IO)
  976.     bne    .End
  977.     move.l    A_IO,a1
  978.     SYS    ReplyMsg
  979. .End:    movem.l    (sp)+,d7/a3-a6
  980.     rts
  981. .Queue:    bclr    #IOB_QUICK,IO_FLAGS(A_IO)    ;We did NOT complete this quickly
  982.     lea    TaskPort(A_DEVICE),a0
  983.     move.l    A_IO,a1
  984.     SYS    PutMsg
  985.     bra    .End
  986. .BadCmd:    move.b    #IOERR_NOCMD,IO_ERROR(a1)
  987.     bra    .Reply
  988.  
  989. AbortIO:
  990.  
  991. ;A6 - device ptr
  992. ;A1 - IORequest
  993.  
  994.     move.b    #IOERR_ABORTED,IO_ERROR(a1)    ;We always say we succeed(ed)
  995.     moveq    #0,d0    ;another success code
  996.     rts
  997.  
  998. ;**************** Device command (IO_COMMAND) routines *************************
  999.  
  1000. ;Note: A6 = ExecBase upon entering a command routine.
  1001. ;The low-level routines load $DFF000 into A6 when needed.
  1002.  
  1003. SaveRegs    equrl    d0-d3/d6-d7/a0-a2/a6
  1004.  
  1005. Read:
  1006.     PUTDEBUG    <'Read: Called'>
  1007.     movem.l    SaveRegs,-(sp)
  1008.     bsr    RWSetup
  1009.     tst.l    d7
  1010.     bne    FinishRW
  1011.  
  1012.     bsr    DISK_Update    ;required because of complex write scheme
  1013. ;Check for sector label nonsense
  1014.     btst    #7,IO_COMMAND(A_IO)
  1015.     beq    .NoLabel
  1016.     move.l    IOTD_SECLABEL(A_IO),d2
  1017.     bne    ReadSecLabel
  1018. .NoLabel:
  1019.  
  1020.     bsr    DISK_Read
  1021.     PUTDEBUG    <'Read: Done'>
  1022.     bra    FinishRW
  1023. Format:
  1024. Write:    movem.l    SaveRegs,-(sp)
  1025.     bsr    RWSetup
  1026.     tst.l    d7
  1027.     bne    FinishRW
  1028.  
  1029. ;Check for sector label nonsense
  1030.     btst    #7,IO_COMMAND(A_IO)
  1031.     beq    .NoLabel
  1032.     move.l    IOTD_SECLABEL(A_IO),d2
  1033.     bne    WriteSecLabel
  1034. .NoLabel:
  1035.  
  1036.     bsr    DISK_Write
  1037. FinishRW:    clr.l    IO_ACTUAL(A_IO)
  1038.     move.b    d7,IO_ERROR(A_IO)
  1039.     bne    .Skip
  1040.     move.l    IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
  1041. .Skip:    movem.l    (sp)+,SaveRegs
  1042.     rts
  1043. RWSetup:
  1044.     move.l    #512*11,d6
  1045.     move.l    #_custom,a6
  1046.     move.l    IO_LENGTH(A_IO),d0
  1047.     move.l    IO_OFFSET(A_IO),d1
  1048.     move.l    IO_DATA(A_IO),a0
  1049.     move.l    d1,d2    ;offset
  1050.     add.l    d0,d2    ;length
  1051.     cmp.l    #901120,d2
  1052.     bhi    .Error
  1053.     bsr    SelectDrive
  1054.     bra    SelectSide
  1055. .Error:    moveq    #DISK_BadParameter,d7
  1056.     rts
  1057.  
  1058. ;Here are the routines for dealing with read/write requests that involve
  1059. ;the sector label. Note that, unlike the usual read/write routines of my
  1060. ;trackdisk, the normal parameter restrictions (e.g. IO_OFFSET and IO_LENGTH
  1061. ;must be a multiple of 512) MUST be observed. And I don't check for illegal
  1062. ;parameters, either. These routines are optimized for compactness rather
  1063. ;than performance because they are rarely (if ever) used.
  1064. ;Enter with:
  1065. ;IO_LENGTH: D0
  1066. ;IO_OFFSET: D1
  1067. ;IO_DATA:   A0
  1068.  
  1069. ReadSecLabel:
  1070.     PUTDEBUG    <'READSECLABEL!'>
  1071.  
  1072.     move.l    d2,a1
  1073.     move.l    d0,d2
  1074.     move.l    #512,d0
  1075. .Read:    bsr    DISK_Read
  1076.     move.l    d1,d3
  1077.     add.l    d0,d1
  1078.     add.l    d0,a0
  1079.     divu.w    d6,d3    ;offset/tracksize
  1080.     swap    d3    ;remainder=sector# (in bytes)
  1081.     lsr.w    #5,d3    ;divide by 32 to get label offset
  1082.     lea    SectorLabels(A_DEVICE),a2
  1083.     lea    (a2,d3.w),a2    ;get pointer to label
  1084.     moveq    #3,d3
  1085. ..    move.l    (a2)+,(a1)+    ;copy label
  1086.     dbra    d3,..
  1087.     sub.l    d0,d2
  1088.     bne    .Read
  1089.     bra    FinishRW
  1090.  
  1091. WriteSecLabel:
  1092.     PUTDEBUG    <'WRITESECLABEL!'>
  1093.  
  1094.     move.l    d2,a1
  1095.     move.l    d0,d2
  1096. .Write:
  1097.     move.l    d1,d3
  1098.     divu.w    d6,d3    ;offset/tracksize
  1099.     swap    d3    ;remainder=sector# (in bytes)
  1100.     lsr.w    #5,d3    ;divide by 32 to get label offset
  1101.  
  1102. ;We check to see whether we can write a full track. (Somehow, I can't
  1103. ;ignore performance completely :-)).
  1104.     tst.w    d3
  1105.     bne    .Read
  1106.     cmp.l    d6,d0
  1107.     bhs    .FullTrack
  1108. .Read:    moveq    #0,d0
  1109.     bsr    DISK_Read    ;force a track read
  1110.     lea    SectorLabels(A_DEVICE),a2
  1111.     lea    (a2,d3.w),a2    ;get pointer to label
  1112.     moveq    #3,d3
  1113. ..    move.l    (a1)+,(a2)+    ;copy label
  1114.     dbra    d3,..
  1115.     move.l    #512,d0
  1116. .L1:    bsr    DISK_Write
  1117.     add.l    d0,d1
  1118.     add.l    d0,a0
  1119.     sub.l    d0,d2
  1120.     bne    .Write
  1121.     bra    FinishRW
  1122. .FullTrack:
  1123.     lea    SectorLabels(A_DEVICE),a2
  1124.     moveq    #((16*11)/4)-1,d3
  1125. ..    move.l    (a1)+,(a2)+    ;copy all sector labels
  1126.     dbra    d3,..
  1127.     move.l    d6,d0
  1128.     bra    .L1
  1129.  
  1130. Update:    move.l    d7,-(sp)
  1131.     moveq    #0,d7
  1132.     bsr    DISK_Update
  1133.     move.b    d7,IO_ERROR(A_IO)
  1134.     move.l    (sp)+,d7
  1135.     rts
  1136.  
  1137. Clear:
  1138. ;This routine marks the track buffer as invalid.
  1139.     st    BufferDrive(A_DEVICE)
  1140.     bclr    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  1141.     rts
  1142.  
  1143. Motor:
  1144. ;Controls the drive motor
  1145. ;If IO_LENGTH=1, motor on
  1146. ;   IO_LENGTH=0, motor off
  1147. ;Old motor state (0=off, anything else means on) stored in IO_ACTUAL.
  1148.  
  1149.     push    d2
  1150.     clr.l    IO_ACTUAL(A_IO)
  1151.     move.b    UnitNum(A_UNIT),d2
  1152.     btst    d2,MotorState(A_DEVICE)
  1153.     beq    .WasOff
  1154.     move.l    #1,IO_ACTUAL(A_IO)
  1155. .WasOff:    tst.l    IO_LENGTH(A_IO)
  1156.     beq    .Off
  1157. ;Turn motor on
  1158.     bsr    SelectDrive
  1159.     bra    .End
  1160. .Off:
  1161. ;Turn motor off
  1162.     bclr    d2,MotorState(A_DEVICE)
  1163.     bsr    Deselect
  1164.     bset    #CIAB_DSKMOTOR,ciabprb    ;motor off
  1165.     addq.b    #3,d2
  1166.     bclr    d2,ciabprb    ;select drive X
  1167. .End:    pop    d2
  1168.     rts
  1169.  
  1170. ChangeState:
  1171. ;Returns whether there is a disk currently in the drive
  1172. ;IO_ACTUAL=0 if disk in drive
  1173. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1174.  
  1175.     clr.l    IO_ACTUAL(A_IO)
  1176.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  1177.     bne    .End
  1178.     move.l    #1,IO_ACTUAL(A_IO)    ;no disk in drive
  1179. .End:    rts
  1180.  
  1181. ChangeNum:
  1182. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1183.     move.l    TDU_COUNTER(A_UNIT),IO_ACTUAL(A_IO)
  1184.     rts
  1185.  
  1186. ProtStatus:
  1187. ;IO_ACTUAL=0 if disk is not write protected
  1188. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1189.  
  1190. ;Very important for compatibility: we must return an error (disk changed)
  1191. ;if there's no disk in the drive.
  1192.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  1193.     bne    .OK
  1194.     move.b    #TDERR_DiskChanged,IO_ERROR(A_IO)
  1195.     rts
  1196.  
  1197. .OK:    clr.l    IO_ACTUAL(A_IO)
  1198.     btst    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  1199.     beq    .End    ;not write protected
  1200.     move.l    #1,IO_ACTUAL(A_IO)    ;no disk in drive
  1201. .End:    rts
  1202.  
  1203. GetDriveType:
  1204. ;Returns type of drive in IO_ACTUAL
  1205. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1206.     move.l    #DRIVE3_5,IO_ACTUAL(A_IO)
  1207.     rts
  1208.  
  1209. GetNumTracks:
  1210. ;Returns number of tracks (note: not cylinders) in IO_ACTUAL
  1211. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1212.     move.l    #160,IO_ACTUAL(A_IO)
  1213.     rts
  1214.  
  1215. GetGeometry:
  1216. ;Fills in DriveGeometry structure pointed to by IO_DATA
  1217.     push    a0
  1218.     move.l    IO_DATA(A_IO),a0
  1219.     move.l    #512,dg_SectorSize(a0)    ;in bytes
  1220.     move.l    #1760,dg_TotalSectors(a0)    ;total # of sectors on drive
  1221.     move.l    #80,dg_Cylinders(a0)    ;number of cylinders
  1222.     move.l    #22,dg_CylSectors(a0)    ;number of sectors/cylinder
  1223.     move.l    #2,dg_Heads(a0)    ;number of surfaces
  1224.     move.l    #11,dg_TrackSectors(a0)    ;number of sectors/track
  1225.     clr.l    dg_BufMemType(a0)    ;preferred buffer memory type
  1226.     clr.b    dg_DeviceType(a0)    ;codes as defined in the SCSI-2 spec
  1227.     move.b    #DGF_REMOVABLE,dg_Flags(a0)    ;flags, including removable
  1228.     clr.w    dg_Reserved(a0)
  1229.     pop    a0
  1230.     rts
  1231.  
  1232. MyStop:    bset    #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
  1233.     rts
  1234. Start:    movem.l    d0-d1/a0-a1,-(sp)
  1235.     bset    #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
  1236.     moveq    #0,d0
  1237.     move.b    MP_SIGBIT+TaskPort(A_DEVICE),d1
  1238.     bset    d1,d0
  1239.     lea    Task(A_DEVICE),a1
  1240.     SYS    Signal
  1241.     movem.l    (sp)+,d0-d1/a0-a1
  1242.     rts
  1243.  
  1244. ;Note: This seek routine can't really be depended on by user programs,
  1245. ;because the disk.resource will corrupt the side select bit.
  1246. TDSeek:    push    d3
  1247.     bsr    SelectDriveSameMotor
  1248.     move.l    IO_OFFSET(A_IO),d3
  1249.     divu.w    #512*11,d3
  1250.     cmp.w    #159,d3
  1251.     bhi    .Error
  1252.     bsr    Seek
  1253. .End:    pop    d3
  1254.     rts
  1255. .Error:    move.b    #TDERR_NotSpecified,IO_ERROR(A_IO)
  1256.     bra    .End
  1257.  
  1258. TDRawRead:
  1259.     movem.l    d0-d1/d3/a6,-(sp)
  1260.     move.l    #_custom,a6
  1261.     moveq    #0,d1
  1262.     bsr    DoRaw
  1263.     movem.l    (sp)+,d0-d1/d3/a6
  1264.     rts
  1265.  
  1266. RawWrite:
  1267.     movem.l    d0-d1/d3/a6,-(sp)
  1268.     move.l    #_custom,a6
  1269.     btst    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  1270.     bne    .WPErr
  1271.     move.w    #1<<14,d1
  1272.     bsr    DoRaw
  1273.     move.l    #3*1000,d0
  1274.     bsr    delay    ;post-write delay
  1275. .End:    movem.l    (sp)+,d0-d1/d3/a6
  1276.     rts
  1277. .WPErr:    move.b    #TDERR_WriteProt,IO_ERROR(A_IO)
  1278.     bra    .End
  1279.  
  1280. DoRaw:
  1281. ;Enter with bit 14 set in d1 according to read/write (for dsklen)
  1282. ;Uses d0-d1/d3 (not saved)
  1283. ;Assumes $dff000 in A6
  1284.  
  1285.     clr.l    IO_ACTUAL(A_IO)
  1286.     bsr    SelectDrive
  1287.     move.l    IO_OFFSET(A_IO),d3
  1288.     cmp.l    #159,d3
  1289.     bhi    .Err
  1290.     bsr    Seek
  1291.     tst.l    d7
  1292.     bne    .Err
  1293.  
  1294.     move.l    IO_DATA(A_IO),dskpt(a6)
  1295.     move.w    #ADKF_WORDSYNC,adkcon(a6)
  1296.     btst    #IOTDB_WORDSYNC,IO_FLAGS(A_IO)
  1297.     beq    .SkipSync
  1298.     move.w    #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
  1299. .SkipSync:
  1300.     move.w    #ADKF_MSBSYNC,adkcon(a6)
  1301.     move.w    #ADKF_SETCLR+ADKF_FAST+ADKF_MFMPREC,adkcon(a6)
  1302.     bsr    PreComp
  1303.     move.w    #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6)    ;enable disk DMA
  1304.     move.w    #$4489,dsksync(a6)    ;set magic sync word
  1305.     bsr    ClearSigs
  1306.     move.w    #INTF_SETCLR+INTF_DSKBLK,intena(a6)    ;enable int
  1307.     move.l    IO_LENGTH(A_IO),d0
  1308.  
  1309.     cmp.l    #32766,d0
  1310.     bhi    .Error
  1311.     lsr.l    #1,d0
  1312.     bset    #15,d0
  1313.     or.w    d1,d0
  1314.  
  1315.     btst    #IOTDB_INDEXSYNC,IO_FLAGS(A_IO)
  1316.     beq    .NoIndex
  1317.     move.w    d0,IndexDskLen(A_DEVICE)
  1318.     bsr    EnableIndex
  1319.     bra    .Wait
  1320. .NoIndex:
  1321.  
  1322.     move.w    d0,dsklen(a6)
  1323.     move.w    d0,dsklen(a6)
  1324. .Wait:    move.l    BlockSig(A_DEVICE),d0
  1325.     move.l    #900*1000,d1
  1326.     bsr    TimeOutWait
  1327.     beq    .Error
  1328.     move.w    #0,dsklen(a6)
  1329.     move.w    #INTF_DSKBLK,intena(a6)    ;disable int
  1330.     bsr    DisableIndex
  1331.     bsr    ClearSigs
  1332. .End:    tst.b    IO_ERROR(A_IO)
  1333.     bne    .EndRts
  1334.     move.l    IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
  1335. .EndRts:    rts
  1336. .Error:    bsr    StopDMA
  1337. .Err:    move.b    #TDERR_NotSpecified,IO_ERROR(A_IO)
  1338.     bra    .End
  1339.  
  1340. AddChangeInt:
  1341. ;This command uses the linkage fields of the IORequest to add a ChangeInt
  1342. ;request to a list maintained in the unit structure. This must NOT be
  1343. ;ReplyMsg'ed to avoid destroying the linkage fields - a special compare in
  1344. ;the task code handles this.
  1345.     movem.l    d0-d1/a0-a1,-(sp)
  1346.     move.l    A_IO,a1
  1347.     lea    ChangeIntList(A_UNIT),a0
  1348.     SYS    AddHead    ;could use any list add routine
  1349.     movem.l    (sp)+,d0-d1/a0-a1
  1350.     rts
  1351.  
  1352. RemChangeInt:
  1353. ;This command unlinks the AddChangeInt request from the task's port.
  1354. ;This is (and must be) an immediate command.
  1355. ;Note that because this command occurs asyncronously to the task code, the
  1356. ;task code that scans the ChangeInt list must be protected with a Forbid.
  1357.  
  1358.     movem.l    d0-d1/a0-a1,-(sp)
  1359.     SYS    Forbid
  1360.     move.l    A_IO,a1
  1361.     SYS    Remove
  1362.     SYS    Permit
  1363.     movem.l    (sp)+,d0-d1/a0-a1
  1364.     rts
  1365.  
  1366. TDRemove:
  1367. ;This command is obsolete, but is unfortuntely used by the ROM filesystem.
  1368. ;It accepts an Interrupt structure (for Cause) in IO_DATA. Only one user per
  1369. ;unit is permitted.
  1370.  
  1371.     tst.l    TDRemoveInt(A_UNIT)
  1372.     beq    .Ok
  1373.     move.b    #TDERR_DriveInUse,IO_ERROR(A_IO)
  1374.     rts
  1375. .Ok:    move.l    IO_DATA(A_IO),TDRemoveInt(A_UNIT)
  1376.     rts
  1377.  
  1378. GetDrive:
  1379. ;Obtain the use of this unit via disk.resource.
  1380.  
  1381. ;    PUTDEBUG    <'GetDrive: Called'>
  1382.     movem.l    d0-d1/a0-a2/a6,-(sp)
  1383.     lea    DiskResourceUnit(A_DEVICE),a2
  1384. .GetUnit:
  1385.     move.l    DiskResourceBase(A_DEVICE),a6
  1386.     move.l    a2,a1
  1387.     jsr    DR_GETUNIT(a6)
  1388.     tst.l    d0
  1389.     bne    .End
  1390.     lea    DiskResourcePort(A_DEVICE),a0
  1391.     move.l    4,a6
  1392.     SYS    WaitPort
  1393.     bra    .GetUnit
  1394. .End:
  1395.  
  1396.     IFEQ    TYPE-DISK
  1397.     move.l    TDUnit(A_UNIT),d0
  1398.     beq    .EndEnd
  1399.     move.l    d0,a0
  1400.     move.w    TDU_CURRTRK(a0),TDU_CURRTRK(A_UNIT)
  1401.     ENDC
  1402.  
  1403. .EndEnd:    movem.l    (sp)+,d0-d1/a0-a2/a6
  1404. ;    PUTDEBUG    <'GetDrive: Done'>
  1405.     rts
  1406.  
  1407. FreeDrive:
  1408. ;Release this unit to other users of disk.resource.
  1409.  
  1410.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1411.  
  1412.     IFEQ    TYPE-DISK
  1413.     move.l    TDUnit(A_UNIT),d0
  1414.     beq    .Skip
  1415.     move.l    d0,a0
  1416.     move.w    TDU_CURRTRK(A_UNIT),TDU_CURRTRK(a0)
  1417. .Skip:
  1418.     ENDC
  1419.  
  1420.     move.l    DiskResourceBase(A_DEVICE),a6
  1421.     jsr    DR_GIVEUNIT(a6)
  1422.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1423.     rts
  1424.  
  1425. ;***************************** Low-level disk code **************************
  1426.  
  1427. ;Standard register usage:
  1428. ;A6            - $dff000
  1429. ;A5 (A_DEVICE) - device ptr
  1430. ;A4 (A_UNIT)   - unit ptr
  1431. ;A3 (A_IO)     - pointer to IO request
  1432.  
  1433. ;For reading/writing:
  1434. ;D0.L - length (bytes)
  1435. ;D1.L - offset (bytes)
  1436. ;A0.L - buffer
  1437.  
  1438. ;NOTE: Error code returned in D7.L - 0 if ok, else error
  1439.  
  1440. ;General errors
  1441. DISK_BadParameter    equ    TDERR_NotSpecified
  1442.  
  1443. ;Read errors
  1444. DISK_NoSync    equ    TDERR_NoSecHdr
  1445. DISK_BadHeader    equ    TDERR_BadHdrSum
  1446. DISK_BadData    equ    TDERR_BadSecSum
  1447.  
  1448. ;Write errors
  1449. DISK_WriteProtected    equ    TDERR_WriteProt
  1450. DISK_VerifyError    equ    TDERR_NotSpecified
  1451.  
  1452.     IFND    dskpt
  1453. dskpt    equ    dskpth
  1454.     ENDC
  1455.  
  1456. ;Note: This is a public value, but it is NOT meant to be changed!!!
  1457. DISK_RawBufSize    equ    13630
  1458.  
  1459. ciabprb    equ    $bfd100
  1460. ciaapra    equ    $bfe001
  1461. ciabddrb    equ    $bfd300
  1462. ciaaddra    equ    $bfe201
  1463.  
  1464. ***************************************************************
  1465. ;Important disk parameters, summary:
  1466.  
  1467. ;Step rate: 3ms. 4ms when looking for track zero.
  1468. ;Settle time: 15ms
  1469. ;Post-write delay: 3ms (officially 1.3ms)
  1470. ;Side select delay: 2ms (officially 1.3ms)
  1471. ***************************************************************
  1472.  
  1473. DISK_Read:
  1474.     movem.l    d0-d5/a0/a1,-(sp)
  1475.  
  1476.     move.l    a0,a1    ;destination ptr in A1
  1477.     move.l    d0,d5
  1478.  
  1479. ;The meat of the read routine...
  1480.  
  1481. .ReadLoop:
  1482.     move.l    d1,d3
  1483.     divu.w    d6,d3    ;d3.w is track #
  1484.     move.l    d3,d4
  1485.     clr.w    d4
  1486.     swap    d4    ;d4.l is byte offset into track
  1487.     bsr    MinSeek
  1488.     tst.l    d4    ;any offset?
  1489.     bne    .Complex    ;yes
  1490.     cmp.l    d6,d5    ;check remaining length
  1491.     blo    .Complex    ;if not a track, forget it
  1492.     move.l    a1,d0
  1493.     btst    #0,d0    ;word aligned?
  1494.     bne    .Complex    ;no, do it the long way
  1495.     move.l    a1,a0
  1496.     bsr    ReadTrackAndDecodeNoBuffer
  1497.     tst.l    d7
  1498.     bne    .End
  1499.     sub.l    d6,d5    ;update length
  1500.     beq    .End
  1501.     add.l    d6,a1    ;update destination pointer
  1502.     add.l    d6,d1    ;update offset
  1503.     bra    .ReadLoop
  1504.  
  1505. .Complex:
  1506.     move.l    DecodedBuffer(A_DEVICE),a0
  1507.     bsr    ReadTrackAndDecodeBuffered
  1508.     tst.l    d7
  1509.     bne    .End
  1510.     add.l    d4,a0
  1511.     sub.l    d6,d4
  1512.     neg.l    d4
  1513.     add.l    d4,d1    ;update offset
  1514.  
  1515. ;D4.L - number of bytes that could be transferred from this track
  1516. ;D5.L - number of bytes left in the entire Read request
  1517.  
  1518.     cmp.l    d5,d4
  1519.     bhs    .FinishUp
  1520.     sub.l    d4,d5    ;update length
  1521.     move.l    d4,d0
  1522.     bsr    CopyMem
  1523.     add.l    d0,a1    ;update destination pointer
  1524.     bra    .ReadLoop
  1525. .FinishUp:
  1526.     move.l    d5,d0
  1527.     bsr    CopyMem    ;use number of bytes left in entire request
  1528. .End:    movem.l    (sp)+,d0-d5/a0/a1
  1529.     rts
  1530.  
  1531. SelectDrive:
  1532. ;Selects drive AND turns on motor
  1533. ;Selects UnitNum(A_UNIT).
  1534.  
  1535.     movem.l    d0/d2,-(sp)
  1536.     move.b    UnitNum(A_UNIT),d2
  1537.     bsr    Deselect
  1538.     bclr    #CIAB_DSKMOTOR,ciabprb    ;motor on
  1539.     move.l    d2,d0
  1540.     addq.b    #3,d0
  1541.     bclr    d0,ciabprb    ;select drive X
  1542.     bset    d2,MotorState(A_DEVICE)
  1543.  
  1544.     moveq    #4,d2
  1545. .Wait:    btst    #CIAB_DSKRDY,ciaapra
  1546.     beq    .End
  1547.     move.l    #100*1000,d0
  1548.     bsr    delay
  1549.     dbra    d2,.Wait    
  1550.  
  1551. .End:    movem.l    (sp)+,d0/d2
  1552.     rts
  1553.  
  1554. Deselect:    or.b    #CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3,ciabprb    ;Deselect all drives
  1555.     rts
  1556.  
  1557. SelectDriveSameMotor:
  1558. ;Select a drive WITHOUT changing the current motor state
  1559. ;Selects UnitNum(A_UNIT).
  1560.  
  1561.     push    d2
  1562.     move.b    UnitNum(A_UNIT),d2
  1563.     bsr    Deselect
  1564.     bclr    #CIAB_DSKMOTOR,ciabprb    ;motor on
  1565.     btst    d2,MotorState(A_DEVICE)
  1566.     bne    .WasOn
  1567.     bset    #CIAB_DSKMOTOR,ciabprb    ;motor off
  1568. .WasOn:    addq.b    #3,d2
  1569.     bclr    d2,ciabprb    ;select drive X
  1570.     pop    d2
  1571. _RTS:    rts
  1572.  
  1573. DISK_Update:
  1574. ;Flush track buffer if "dirty"
  1575.  
  1576.     bclr    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  1577.     beq    _RTS
  1578.     movem.l    d0-d2/a0/a2/a6,-(sp)
  1579.     move.l    #_custom,a6
  1580.  
  1581. ;This is somewhat tricky, because the currently selected drive may not be
  1582. ;the drive that we want to write to!
  1583.     move.b    UnitNum(A_UNIT),d0
  1584.     move.w    TDU_CURRTRK(A_UNIT),d1
  1585.     move.b    BufferDrive(A_DEVICE),UnitNum(A_UNIT)    ;fake drive
  1586.     move.b    BufferTrack(A_DEVICE),TDU_CURRTRK+1(A_UNIT)    ;fake track
  1587.     bsr    SelectDrive
  1588.     bsr    SelectSide
  1589.  
  1590. ;The logic of this routine is complicated by the need to support the write
  1591. ;optimization scheme (see DISK_Write for details). What we need to do is
  1592. ;check the WriteMap to determine whether a ReadTrackAndDecode is required
  1593. ;before finally writing...
  1594.     move.l    WriteMap(A_DEVICE),d2
  1595.     and.l    #%11111111111,d2    ;changes for different drive types!
  1596.     cmp.l    #%11111111111,d2    ;changes for different drive types!
  1597.     beq    .SkipRead
  1598.     move.l    DecodedBuffer(A_DEVICE),a0
  1599.     bsr    ReadTrackAndDecode
  1600.     tst.l    d7
  1601.     bne    .End
  1602. .SkipRead:
  1603.     move.l    d7,WriteMap(A_DEVICE)
  1604.  
  1605.     move.l    DecodedBuffer(A_DEVICE),a2
  1606.     bsr    EncodeAndWriteTrack
  1607.     move.b    d0,UnitNum(A_UNIT)
  1608.     move.w    d1,TDU_CURRTRK(A_UNIT)
  1609.     bsr    SelectDrive
  1610. .End:    movem.l    (sp)+,d0-d2/a0/a2/a6
  1611.     rts
  1612.  
  1613. SelectSide:
  1614. ;Select the correct side based on current track. This routine MUST be
  1615. ;called by certain routines (e.g. Update, Write) because the side select is
  1616. ;lost after a GiveUnit!
  1617.     bclr    #CIAB_DSKSIDE,ciabprb    ;set to upper
  1618.     btst    #0,TDU_CURRTRK+1(A_UNIT)
  1619.     bne    .skip
  1620.     bset    #CIAB_DSKSIDE,ciabprb    ;set to lower
  1621. .skip:    push    d0
  1622.     move.l    #2*1000,d0
  1623.     bsr    delay
  1624.     pop    d0
  1625.     rts
  1626.  
  1627. ;Enter with destination track in d3.b
  1628.  
  1629. ;Note: This routine checks for valid track numbers -- if either the
  1630. ;current track number or the destination is invalid, PANIC.
  1631.  
  1632. MinSeek:
  1633. ;Only call MinSeek if it is certain that the side select has not been
  1634. ;changed!
  1635.     cmp.b    TDU_CURRTRK+1(A_UNIT),d3
  1636.     bne    Seek
  1637.     move.w    d0,-(sp)
  1638.     move.b    BufferDrive(A_DEVICE),d0
  1639.     cmp.b    UnitNum(A_UNIT),d0
  1640.     bne    .Seek
  1641.     move.w    (sp)+,d0
  1642.     rts
  1643. .Seek:    move.w    (sp)+,d0
  1644.  
  1645. Seek:    movem.l    d0/d3-d4,-(sp)
  1646.  
  1647.     bsr    DISK_Update
  1648.     tst.l    d7
  1649.     bne    .End
  1650.  
  1651.     move.l    TDU_STEPDELAY(A_UNIT),d0
  1652.     cmp.b    #160,d3
  1653.     bhs    .Error
  1654.  
  1655. ;Set head
  1656.     bclr    #CIAB_DSKSIDE,ciabprb    ;set to upper
  1657.     btst    #0,d3
  1658.     bne    .skip
  1659.     bset    #CIAB_DSKSIDE,ciabprb    ;set to lower
  1660. .skip:
  1661.     move.w    TDU_CURRTRK(A_UNIT),d4
  1662.     lsr.b    #1,d4
  1663.     cmp.b    #80,d4
  1664.     bhs    .Error
  1665.     move.b    d3,TDU_CURRTRK+1(A_UNIT)
  1666.     lsr.b    #1,d3
  1667.     bset    #CIAB_DSKDIREC,ciabprb    ;set to "out" (lower tracks)
  1668.     sub.b    d3,d4
  1669.     beq    .SideSelectOnly
  1670.     bhi    .StepIn    ;Go if DISK_CurrentTrack > Destination
  1671.     bclr    #CIAB_DSKDIREC,ciabprb    ;set to "in" (higher tracks)
  1672.     neg.b    d4
  1673. .StepIn:
  1674.     bsr    SelectDriveSameMotor
  1675.     bset    #CIAB_DSKSTEP,ciabprb
  1676.     bclr    #CIAB_DSKSTEP,ciabprb    ;step head
  1677.     bset    #CIAB_DSKSTEP,ciabprb
  1678.     bsr    Deselect
  1679.     bsr    delay
  1680.     subq.b    #1,d4
  1681.     bne    .StepIn
  1682.     move.l    TDU_SETTLEDELAY(A_UNIT),d0
  1683.     bsr    delay
  1684.     bsr    SelectDriveSameMotor
  1685. .End:    movem.l    (sp)+,d0/d3-d4
  1686.     rts
  1687. .SideSelectOnly:
  1688.     move.l    #2*1000,d0
  1689.     bsr    delay
  1690.     bra    .End
  1691. .Error:
  1692. ;    move.w    #$f00,$dff180
  1693.     bra    .Error
  1694.  
  1695. ;*************************** Delay code ****************************
  1696.  
  1697. delay:
  1698.  
  1699. ;Enter with microseconds in D0.L
  1700.  
  1701.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1702.     lea    TimerIORequest(A_DEVICE),a1
  1703.     move.w    #TR_ADDREQUEST,IO_COMMAND(a1)
  1704.     clr.l    TV_SECS+IO_SIZE(a1)
  1705.     move.l    d0,TV_MICRO+IO_SIZE(a1)
  1706.     move.l    4,a6
  1707.     SYS    DoIO
  1708.  
  1709. ;Clear signal bit (TimeOutWait depends on the signal bit being 'correct').
  1710.     lea    TimerIORequest(A_DEVICE),a1
  1711.     move.l    MN_REPLYPORT(a1),a0
  1712.     move.b    MP_SIGBIT(a0),d0
  1713.     moveq    #0,d1
  1714.     bset    d0,d1
  1715.     moveq    #0,d0
  1716.     SYS    SetSignal
  1717.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1718.     rts
  1719.  
  1720. TimeOutWait:
  1721.  
  1722. ;Wait for signal mask in D0, but include a time-out specified in D1 (usecs).
  1723. ;Returns wait mask in D0, or 0 if a time-out occured (Z flag will be set).
  1724.  
  1725.     movem.l    d1-d4/a0-a2/a6,-(sp)
  1726.     move.l    d0,d3
  1727.     move.l    4,a6
  1728.     lea    TimerIORequest(A_DEVICE),a1
  1729.     move.l    a1,a2
  1730.     move.w    #TR_ADDREQUEST,IO_COMMAND(a1)
  1731.     clr.l    TV_SECS+IO_SIZE(a1)
  1732.     move.l    d1,TV_MICRO+IO_SIZE(a1)
  1733.     move.l    MN_REPLYPORT(a1),a0
  1734.     moveq    #0,d2
  1735.     move.b    MP_SIGBIT(a0),d1
  1736.     bset    d1,d2
  1737.     SYS    SendIO
  1738.     move.l    d3,d0
  1739.     add.l    d2,d0    ;equivilent to OR in this case
  1740.     SYS    Wait
  1741.     and.l    d3,d0    ;exclude timer signal
  1742.     move.l    d0,d4
  1743.     beq    .TimeOut    ;if zero, timer signal was the only signal
  1744.     move.l    a2,a1
  1745.     SYS    AbortIO
  1746. .TimeOut:    move.l    a2,a1
  1747.     SYS    WaitIO
  1748.     moveq    #0,d0    ;value
  1749.     move.l    d2,d1    ;mask (timer sig)
  1750.     SYS    SetSignal    ;make SURE the timer signal is clear
  1751.     move.l    d4,d0
  1752.     movem.l    (sp)+,d1-d4/a0-a2/a6
  1753.     rts
  1754.  
  1755. ;*************************** End delay code ****************************
  1756.  
  1757. ;*************************** Interrupt routines ************************
  1758.  
  1759. ;------------------------------------------
  1760. ;Register contents upon entering a handler:
  1761. ;D1 - (INTENAR) AND (INTREQR)
  1762. ;A0 - $dff000
  1763. ;A1 - data ptr (device ptr in this case)
  1764. ;A6 - ExecBase 
  1765.  
  1766. ;d0, d1, a0, a1, a5, and a6 are scratch.
  1767. ;-----------------------------------------
  1768.  
  1769. ;However, the disk.resource autodoc indicates the following arrangement for
  1770. ;interrupts installed by GetUnit:
  1771.  
  1772. ;D0/D1/A0/A1 are scratch
  1773. ;A1 points to IS_DATA (device pointer in this case).
  1774. ;Make no other assumptions!
  1775.  
  1776. SyncInt:    addq.w    #1,SyncCount(a1)
  1777.     move.l    SyncSig(a1),d0
  1778.     lea    Task(a1),a1
  1779.     push    a6
  1780.     move.l    4,a6
  1781.     SYS    Signal
  1782.     pop    a6
  1783.  
  1784. ;Delay approximately 63us to avoid two sync interrupts
  1785. ;This could be improved.
  1786.     moveq    #1,d0
  1787. .L1:    move.b    vhposr+_custom,d1
  1788. .L2:    cmp.b    vhposr+_custom,d1
  1789.     beq    .L2
  1790.     dbra    d0,.L1
  1791.     move.w    #INTF_DSKSYNC,intreq+_custom    ;clear sync
  1792.     rts    
  1793.  
  1794. BlockInt:
  1795.     push    a6
  1796.     move.l    #_custom,a6
  1797.     move.w    #INTF_DSKBLK,intreq(a6)
  1798.  
  1799. ;Test DEVB_Verify flag. If set, initiate a read into the verify buffer.
  1800.     bclr    #DEVB_Verify,DEV_FLAGS(a1)
  1801.     beq    .NoVerify
  1802.  
  1803.     move.w    #INTF_SETCLR+INTF_DSKSYNC,intena(a6)    ;enable sync int
  1804.     move.l    VerifyBuffer(a1),dskpt(a6)
  1805.     move.w    #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
  1806.     move.w    #0,dsklen(a6)
  1807.     move.w    #$9761,dsklen(a6)
  1808.     move.w    #$9761,dsklen(a6)
  1809.     move.w    #$4489,dsksync(a6)    ;set magic sync word
  1810. ;Now the first sync interrupt we get will be known to be valid, because the
  1811. ;DMA read is fully started.
  1812.     bra    .End
  1813.  
  1814. .NoVerify:
  1815.     move.l    BlockSig(a1),d0
  1816.     lea    Task(a1),a1
  1817.     move.l    4,a6
  1818.     SYS    Signal
  1819. .End:    pop    a6
  1820.     rts
  1821.  
  1822. IndexInt:
  1823.     move.w    IndexDskLen(a1),d0
  1824.     beq    .End
  1825.     move.w    d0,dsklen+_custom
  1826.     move.w    d0,dsklen+_custom
  1827.     clr.w    IndexDskLen(a1)
  1828. .End:    rts
  1829.  
  1830. EnableIndex:
  1831.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1832.     move.l    CIABase(A_DEVICE),a6
  1833.     moveq    #16,d0
  1834.     SYS    SetICR    ;clear interrupt
  1835.     move.l    #16+128,d0
  1836.     SYS    AbleICR    ;enable interrupt
  1837.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1838.     rts
  1839. DisableIndex:
  1840.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1841.     move.l    CIABase(A_DEVICE),a6
  1842.     moveq    #16,d0
  1843.     SYS    AbleICR    ;disable interrupt
  1844.     moveq    #16,d0
  1845.     SYS    SetICR    ;clear interrupt
  1846.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1847.     rts
  1848.  
  1849. ;*********************** End interrupt routines ************************
  1850.  
  1851. ClearSigs:
  1852.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1853.     move.w    #INTF_DSKBLK+INTF_DSKSYNC,intreq+_custom    ;clear sync+done
  1854.     clr.w    SyncCount(A_DEVICE)
  1855.     clr.w    IndexDskLen(A_DEVICE)
  1856.     move.l    4,a6
  1857.     moveq    #0,d0    ;value
  1858.     move.l    SyncSig(A_DEVICE),d1
  1859.     add.l    BlockSig(A_DEVICE),d1
  1860.     SYS    SetSignal
  1861.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1862.     rts
  1863.  
  1864. SetRegs:
  1865. ;This routine enables the block interrupt, but leaves sync interrupts
  1866. ;disabled.
  1867.  
  1868.     move.l    RawBuffer(A_DEVICE),dskpt(a6)
  1869.     move.w    #ADKF_MSBSYNC+ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6)
  1870.     move.w    #ADKF_SETCLR+ADKF_FAST+ADKF_WORDSYNC+ADKF_MFMPREC,adkcon(a6)
  1871.     move.w    #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6)    ;enable disk DMA
  1872.     bsr    ClearSigs
  1873.     move.w    #INTF_SETCLR+INTF_DSKBLK,intena(a6)    ;enable block int
  1874.     rts
  1875.  
  1876. PreComp:
  1877. ;Enter with $dff000 in A6
  1878.     movem.l    d0-d1,-(sp)
  1879.     move.w    #ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6)    ;no precomp
  1880.     move.w    TDU_CURRTRK(A_UNIT),d0
  1881.     cmp.w    TDU_COMP01TRACK(A_UNIT),d0
  1882.     bls    .End
  1883.     move.w    #ADKF_SETCLR+ADKF_PRECOMP0,d1
  1884.     cmp.w    TDU_COMP10TRACK(A_UNIT),d0
  1885.     bls    .Done
  1886.     move.w    #ADKF_SETCLR+ADKF_PRECOMP1,d1
  1887.     cmp.w    TDU_COMP11TRACK(A_UNIT),d0
  1888.     bls    .Done
  1889.     move.w    #ADKF_SETCLR+ADKF_PRECOMP0+ADKF_PRECOMP1,d1
  1890. .Done:    move.w    d1,adkcon(a6)
  1891. .End:    movem.l    (sp)+,d0-d1
  1892.     rts
  1893.  
  1894. ScanSync:
  1895. ;Scan for sync mark
  1896. ;A2 - pointer to raw data -- updated
  1897. ;Error code in D7
  1898.  
  1899.     bsr    WaitWordSync
  1900.     tst.l    d7
  1901.     bne    .End
  1902.  
  1903. ;This routine is trickier than it appears. The trick is that we must NOT
  1904. ;assume a $4489 at the beginning of our buffer. This phenomenon occurs when
  1905. ;the DMA starts in the middle of the first sync word. The second sync word
  1906. ;is thrown away by the hardware. It sounds exotic, but it actually happens
  1907. ;quite often!
  1908.  
  1909.     push    a4
  1910.     move.l    RawBuffer(A_DEVICE),a4
  1911.     cmp.l    a4,a2
  1912.     add.w    #DISK_RawBufSize,a4    ;does not affect flags
  1913.     beq    .found    ;if start of buffer, don't scan for sync!!!
  1914.  
  1915. .Sync:    cmpi.w    #$4489,(a2)+
  1916.     beq    .found
  1917.     cmp.l    a2,a4
  1918.     bhi    .Sync
  1919. .Error:    pop    a4
  1920.     moveq    #DISK_NoSync,d7
  1921.     rts
  1922. .found:    cmpi.w    #$4489,(a2)
  1923.     bne    .ok
  1924.     addq.l    #2,a2
  1925.     cmp.l    a2,a4
  1926.     bhi    .found
  1927.     bra    .Error
  1928. .ok:    pop    a4
  1929. .End:    rts
  1930.  
  1931. StopDMA:    move.w    #0,dsklen(a6)
  1932.     move.w    #1<<15,dsklen(a6)
  1933.     move.w    #1<<15,dsklen(a6)    ;zero-length DMA transfer
  1934.     move.w    #INTF_DSKBLK+INTF_DSKSYNC,intena(a6)    ;disable ints
  1935.     bra    ClearSigs
  1936.  
  1937. WaitWordSync:
  1938. ;Wait for a sync mark or disk block done
  1939. ;Will return an error in 300ms if nothing happens.
  1940.  
  1941.     movem.l    d0-d1,-(sp)
  1942. .Wait:    tst.w    SyncCount(A_DEVICE)
  1943.     bne    .Sync
  1944.     move.l    SyncSig(A_DEVICE),d0
  1945.     add.l    BlockSig(A_DEVICE),d0
  1946.     move.l    #300*1000,d1    ;time-out (300ms)
  1947.     bsr    TimeOutWait
  1948.     beq    .Error
  1949.     and.l    BlockSig(A_DEVICE),d0
  1950.     bne    .Done
  1951.     bra    .Wait
  1952. .Sync:    subq.w    #1,SyncCount(A_DEVICE)
  1953. .End:    movem.l    (sp)+,d0-d1
  1954.     rts
  1955. .Done:    move.w    #$C000,SyncCount(A_DEVICE)    ;this is obscure - should change
  1956.     bra    .End
  1957. .Error:    moveq    #DISK_NoSync,d7
  1958.     bra    .End
  1959.  
  1960. ReadTrackAndDecodeBuffered:
  1961.     push    d0
  1962.     move.l    WriteMap(A_DEVICE),d0
  1963.     beq    .OK    ;no optimized writes to worry about
  1964.     and.l    #%11111111111,d0    ;changes for different drive types!
  1965.     cmp.l    #%11111111111,d0    ;changes for different drive types!
  1966.     beq    .End
  1967.     moveq    #-1,d0
  1968.     move.l    d0,WriteMap(A_DEVICE)    ;mark sectors as filled
  1969.     bra    .ReadTrack
  1970.  
  1971. .OK:    move.b    UnitNum(A_UNIT),d0
  1972.     cmp.b    BufferDrive(A_DEVICE),d0
  1973.     bne    .ReadTrack
  1974.     move.w    TDU_CURRTRK(A_UNIT),d0
  1975.     cmp.b    BufferTrack(A_DEVICE),d0
  1976.     bne    .ReadTrack
  1977. .End:    pop    d0
  1978.     rts
  1979. .ReadTrack:
  1980.     pop    d0
  1981.  
  1982. ReadTrackAndDecode:
  1983.  
  1984. ;Input: Buffer ptr in A0
  1985. ;Returns error code in D7
  1986.  
  1987.     move.b    UnitNum(A_UNIT),BufferDrive(A_DEVICE)
  1988.     move.b    TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
  1989.  
  1990. ReadTrackAndDecodeNoBuffer:
  1991.     move.w    d0,-(sp)
  1992.     move.b    TDU_RETRYCNT(A_UNIT),d0
  1993. .Retry:    moveq    #0,d7
  1994.     bsr    ReadTrackAndDecodeNoRetry
  1995.     tst.l    d7
  1996.     beq    .End
  1997.     subq.b    #1,d0
  1998.     bpl    .Retry
  1999. .End:    move.w    (sp)+,d0
  2000.     rts
  2001.  
  2002. ReadTrackAndDecodeNoRetry:
  2003.     movem.l    d0-d6/a0-a2,-(sp)
  2004.  
  2005. ;Initiate a raw track read
  2006. ;Because we are using WORDSYNC interrupts, this code is VERY VERY tricky!!!!!
  2007.  
  2008.     bsr    SetRegs
  2009.     move.w    #0,dsksync(a6)    ;set invalid sync word
  2010. ;At this point sync interrupts will stop happenning, because $0000 will
  2011. ;never occur. So, we clear the interrupt and enable it.
  2012.     bsr    ClearSigs
  2013.     move.w    #INTF_SETCLR+INTF_DSKSYNC,intena(a6)    ;enable sync int
  2014.     move.w    #0,dsklen(a6)
  2015.     move.w    #$9A9E,dsklen(a6)
  2016.     move.w    #$9A9E,dsklen(a6)    ;read approx. 13,628 bytes
  2017.     move.w    #$4489,dsksync(a6)    ;set magic sync word
  2018.  
  2019. ;Now the first sync interrupt we get will be known to be valid, because the
  2020. ;DMA read is fully started.
  2021.  
  2022. ;Decode track
  2023.     move.l    RawBuffer(A_DEVICE),a2
  2024.  
  2025.     moveq    #11-1,d3    ;# of sectors
  2026.     move.l    #$55555555,d2
  2027.  
  2028. ;Wait for first sync marks...
  2029.     bsr    WaitWordSync
  2030.     tst.l    d7
  2031.     bne    .End
  2032.  
  2033. .SecLoop:
  2034.     bsr    ScanSync
  2035.     tst.l    d7
  2036.     bne    .End
  2037.  
  2038.     bsr    DecodeLong    ;get header
  2039.     subq.l    #8,a2
  2040.  
  2041.     and.w    #$ff00,d0    ;mask off sector number
  2042.     move.l    d0,d1
  2043.     cmp.w    #$0a00,d1
  2044.     bhi    .HeaderError
  2045.     add.w    d1,d1    ;convert to sector offset
  2046.     lsr.w    #4,d0    ;get sector label offset
  2047.     lea    SectorLabels(A_DEVICE),a1
  2048.     lea    (a1,d0.w),a1
  2049.  
  2050. ;This code supports the write optimization...
  2051.     lsr.w    #4,d0    ;sector number
  2052.     move.l    WriteMap(A_DEVICE),d4
  2053.     btst    d0,d4    ;should we avoid reading this sector?
  2054.     bne    .EndLoop    ;yes, skip to next sector
  2055.  
  2056.     move.l    d1,-(sp)
  2057.     moveq    #0,d5
  2058.     move.l    (a2)+,d0
  2059.     eor.l    d0,d5
  2060.     move.l    (a2)+,d0
  2061.     eor.l    d0,d5
  2062. ;Decode and checksum sector label
  2063.     moveq    #3,d6
  2064. .Label:    move.l    16(a2),d4
  2065.     eor.l    d4,d5
  2066.     and.l    d2,d4
  2067.     move.l    (a2)+,d1
  2068.     eor.l    d1,d5
  2069.     and.l    d2,d1
  2070.     add.l    d1,d1
  2071.     or.l    d1,d4
  2072.     move.l    d4,(a1)+
  2073.     dbra    d6,.Label
  2074.     and.l    d2,d5
  2075.     lea    16(a2),a2    ;point at header checksum
  2076.     move.l    (sp)+,d1
  2077.     bsr    DecodeLong    ;header checksum
  2078.     cmp.l    d0,d5
  2079.     bne    .HeaderError
  2080.  
  2081. ;Verify track position
  2082.     swap    d1
  2083.     cmp.b    TDU_CURRTRK+1(A_UNIT),d1
  2084.     bne    .WrongTrack
  2085.     swap    d1
  2086.  
  2087.     bsr    DecodeLong    ;data area checksum
  2088.     lea    (a0,d1.w),a1    ;compute destination
  2089.  
  2090. ;Decode and checksum data block
  2091.     moveq    #127,d6
  2092.     moveq    #0,d5
  2093. .L1:    move.l    512(a2),d4
  2094.     eor.l    d4,d5
  2095.     and.l    d2,d4
  2096.     move.l    (a2)+,d1
  2097.     eor.l    d1,d5
  2098.     and.l    d2,d1
  2099.     add.l    d1,d1
  2100.     or.l    d1,d4
  2101.     move.l    d4,(a1)+
  2102.     dbra    d6,.L1
  2103.     and.l    d2,d5
  2104.     lea    512(a2),a2
  2105.  
  2106.     cmp.l    d0,d5    ;check data area checksum
  2107.     bne    .DataError
  2108. .EndLoop:    dbra    d3,.SecLoop
  2109.  
  2110. .End:    bsr    StopDMA
  2111.     movem.l    (sp)+,d0-d6/a0-a2
  2112.     rts
  2113. .HeaderError:
  2114.     bsr    Clear
  2115.     moveq    #DISK_BadHeader,d7
  2116.     bra    .End
  2117. .DataError:
  2118.     bsr    Clear
  2119.     moveq    #DISK_BadData,d7
  2120.     bra    .End
  2121. .WrongTrack:
  2122.     bsr    Clear
  2123.     moveq    #TDERR_SeekError,d7
  2124.     move.w    TDU_CURRTRK(A_UNIT),d3
  2125.     bsr    SeekZero
  2126.     bsr    SelectDrive
  2127.     bsr    Seek
  2128.     bra    .End
  2129.  
  2130. DecodeLong:
  2131. ;A2 - ptr to buffer -- updated
  2132. ;D2 - $55555555
  2133. ;D0 - result
  2134.  
  2135.     move.l    d1,-(sp)
  2136.     move.l    (a2)+,d0
  2137.     move.l    (a2)+,d1
  2138.     and.l    d2,d0
  2139.     and.l    d2,d1
  2140.     add.l    d0,d0    ;was lsl.l #1,d0
  2141.     or.l    d1,d0
  2142.     move.l    (sp)+,d1
  2143.     rts
  2144.  
  2145. EncodeLong:
  2146. ;Enter with data to be encoded in D0.L
  2147. ;and pointer to destination in A0 -- updated
  2148. ;Exit with checksum in D5
  2149.  
  2150.     movem.l    d0-d4,-(sp)
  2151.     moveq    #0,d5
  2152.     move.l    #$55555555,d4
  2153.     move.l    d0,d3
  2154.     lsr.l    #1,d0
  2155.     bsr    Encode
  2156.     move.l    d3,d0
  2157.     bsr    Encode
  2158.     and.l    #$55555555,d5
  2159.     movem.l    (sp)+,d0-d4
  2160.     rts
  2161.  
  2162. Encode:
  2163. ;Enter with longword to code in D0.L and #$55555555 in D4
  2164. ;uses d0,d1,d2,a0 -- not saved
  2165.  
  2166. ;Accumulates checksum in D5
  2167.  
  2168.     and.l    d4,d0
  2169.     move.l    d0,d2
  2170.     eor.l    d4,d2
  2171.     move.l    d2,d1
  2172.     add.l    d2,d2
  2173.     lsr.l    #1,d1
  2174.     bset    #31,d1
  2175.     and.l    d2,d1
  2176.     or.l    d1,d0
  2177.     btst    #0,-1(a0)
  2178.     beq    .ok
  2179.     bclr    #31,d0
  2180. .ok:    eor.l    d0,d5
  2181.     move.l    d0,(a0)+
  2182.     rts
  2183.  
  2184. EncodeBlock:
  2185. ;Destination is always chip RAM (RawBuffer).
  2186. ;Source could be in chip RAM or fast RAM (in A2).
  2187.  
  2188.     movem.l    d0-d1/a0-a1/a6,-(sp)
  2189.     move.l    4,a6
  2190.     move.l    a2,a1
  2191.     SYS    TypeOfMem
  2192.     and.l    #MEMF_CHIP,d0
  2193.     bne    .Chip
  2194.     movem.l    (sp)+,d0-d1/a0-a1/a6
  2195.     bra    EncodeBlockCPU
  2196. .Chip:    movem.l    (sp)+,d0-d1/a0-a1/a6
  2197.     bra    EncodeBlockBlit
  2198.  
  2199. EncodeBlockCPU:
  2200. ;Enter with pointer to source data in A2 -- updated
  2201. ;Enter with pointer to destination in A0 -- updated
  2202.  
  2203. ;Exit with checksum in D5
  2204.     move.l    d6,-(sp)
  2205.     moveq    #0,d5
  2206.     move.w    #(512/4)-1,d6
  2207.  
  2208. EncodeBlockSub:
  2209. ;Number of longwords to encode (minus one) in D6.w
  2210.     movem.l    d0-d4,-(sp)
  2211.  
  2212. ;Encode odd bits
  2213.     push    a2
  2214.     move.w    d6,d3
  2215.     move.l    #$55555555,d4
  2216. .L1:    move.l    (a2)+,d0
  2217.     lsr.l    #1,d0
  2218.     bsr    Encode
  2219.     dbra    d3,.L1
  2220.  
  2221. ;Encode even bits
  2222.     pop    a2
  2223.     move.w    d6,d3
  2224. .L2:    move.l    (a2)+,d0
  2225.     bsr    Encode
  2226.     dbra    d3,.L2
  2227.     and.l    #$55555555,d5
  2228.     movem.l    (sp)+,d0-d4
  2229.     move.l    (sp)+,d6
  2230.     rts
  2231.  
  2232. EncodeSectorLabels:
  2233. ;D5 (checksum) must be initialized by caller
  2234.     move.l    d6,-(sp)
  2235.     move.w    #(16/4)-1,d6
  2236.     bra    EncodeBlockSub
  2237.  
  2238. EncodeBlockBlit:
  2239. ;Enter with pointer to source data in A2 -- updated
  2240. ;Enter with pointer to destination in A0 -- updated
  2241.  
  2242. ;Exit with checksum in D5
  2243.  
  2244. ;Extra blitter equates...
  2245. BLTCPTR        equ    $048
  2246. BLTBPTR        equ    $04c
  2247. BLTAPTR        equ    $050
  2248. BLTDPTR        equ    $054
  2249.  
  2250.     movem.l    d0-d2/a0-a1/a6,-(sp)
  2251.     push    a0
  2252.     move.l    GraphBase(A_DEVICE),a6
  2253.     SYS    OwnBlitter
  2254.     move.l    (sp),a0
  2255.     move.l    #_custom,a1
  2256.  
  2257.     move.w    #$808,d0    ;BLTSIZE
  2258.  
  2259.     SYS    WaitBlit
  2260.  
  2261.     move.w    #$ffff,BLTAFWM(a1)
  2262.     move.w    #$ffff,BLTALWM(a1)
  2263.     clr.w    BLTBMOD(a1)
  2264.     clr.w    BLTAMOD(a1)
  2265.     clr.w    BLTDMOD(a1)
  2266.     move.w    #$5555,BLTCDAT(a1)
  2267.  
  2268.     move.l    a2,BLTBPTR(a1)
  2269.     move.l    a2,BLTAPTR(a1)
  2270.     move.l    a0,BLTDPTR(a1)
  2271.     move.w    #$1db1,BLTCON0(a1)
  2272.     clr.w    BLTCON1(a1)
  2273.     move.w    d0,BLTSIZE(a1)
  2274.  
  2275.     SYS    WaitBlit
  2276.  
  2277.     move.l    a0,BLTBPTR(a1)
  2278.     move.l    a2,BLTAPTR(a1)
  2279.     move.l    a0,BLTDPTR(a1)
  2280.     move.w    #$2d8c,BLTCON0(a1)
  2281.     move.w    d0,BLTSIZE(a1)
  2282.     movem.l    a0/a2,-(sp)
  2283.     lea    510(a2),a2    ;ptr to end of src
  2284.     lea    1022(a0),a0
  2285.  
  2286.     SYS    WaitBlit
  2287.  
  2288.     move.l    a2,BLTBPTR(a1)    ;src end
  2289.     move.l    a2,BLTAPTR(a1)    ;src end
  2290.     move.l    a0,BLTDPTR(a1)    ;dst end
  2291.     move.w    #$0db1,BLTCON0(a1)
  2292.     move.w    #$1002,BLTCON1(a1)    ;decrement
  2293.     move.w    d0,BLTSIZE(a1)
  2294.     movem.l    (sp)+,a0/a2
  2295.     lea    512(a0),a0
  2296.  
  2297.     SYS    WaitBlit
  2298.  
  2299.     move.l    a0,BLTBPTR(a1)
  2300.     move.l    a2,BLTAPTR(a1)
  2301.     move.l    a0,BLTDPTR(a1)
  2302.     move.w    #$1d8c,BLTCON0(a1)
  2303.     clr.w    BLTCON1(a1)
  2304.     move.w    d0,BLTSIZE(a1)
  2305.     pop    a0
  2306.  
  2307.     SYS    WaitBlit
  2308.  
  2309.     bsr    Correct
  2310.     lea    512(a0),a0
  2311.     bsr    Correct
  2312.     lea    -512(a0),a0
  2313.  
  2314.     move.w    #(1024/4)-1,d0
  2315.     move.l    #$55555555,d2
  2316.     moveq    #0,d5
  2317. ..    move.l    (a0)+,d1
  2318.     eor.l    d1,d5
  2319.     dbra    d0,..
  2320.     and.l    d2,d5
  2321.  
  2322.     SYS    DisownBlitter
  2323.     movem.l    (sp)+,d0-d2/a0-a1/a6
  2324.     lea    512(a2),a2    ;update source pointer
  2325.     lea    1024(a0),a0    ;update destination pointer
  2326.     rts
  2327.  
  2328. ;This routine corrects the high bit of the current byte based on the
  2329. ;low bit of the previous byte.
  2330.  
  2331. Correct:
  2332.     push    d0
  2333.     move.b    (a0),d0
  2334.     btst    #0,-1(a0)
  2335.     bne    .ResetClock
  2336.     btst    #6,d0
  2337.     bne    .end
  2338.     bset    #7,d0
  2339.     bra    .end1
  2340. .ResetClock:
  2341.     bclr    #7,d0
  2342. .end1:    move.b    d0,(a0)
  2343. .end:    pop    d0
  2344.     rts
  2345.  
  2346. DISK_Wait:
  2347. ;Assumes $dff000 in A6.
  2348.     movem.l    d0-d1,-(sp)
  2349.     tst.w    SyncCount(A_DEVICE)
  2350.     bmi    .OK    ;if WaitWordSync detected a BlockSig, don't wait!
  2351.     move.l    BlockSig(A_DEVICE),d0
  2352.     move.l    #300*1000,d1
  2353.     bsr    TimeOutWait
  2354.     bne    .OK
  2355.     moveq    #DISK_NoSync,d7
  2356. .OK:    move.w    #INTF_DSKBLK+INTF_DSKSYNC,intena(a6)    ;disable ints
  2357.     bsr    ClearSigs
  2358.     movem.l    (sp)+,d0-d1
  2359.     rts
  2360.  
  2361. EncodeAndWriteTrack:
  2362. ;Enter with pointer to source data in A2
  2363.  
  2364.     movem.l    d0-d6/a0-a2,-(sp)
  2365.  
  2366.     btst    #CIAB_DSKPROT,ciaapra    ;check write protect status
  2367.     beq    .Protected
  2368.  
  2369.     move.l    RawBuffer(A_DEVICE),a0
  2370.  
  2371. ;Gap = 1660 bytes - 2 bytes for hardware bug
  2372.     move.l    #$aaaaaaaa,d1    ;10101010...
  2373.     move.w    #414,d0
  2374. ..    move.l    d1,(a0)+
  2375.     dbra    d0,..
  2376.     subq.l    #2,a0    ;leave room for 2 extra bytes at the very end
  2377.  
  2378.     moveq    #11,d1    ;number of sectors
  2379.     moveq    #0,d3    ;sector count
  2380. .SecLoop:
  2381.     move.l    #$aaaaaaaa,(a0)
  2382.     bsr    Correct
  2383.     addq.l    #4,a0
  2384.     move.l    #$44894489,(a0)+
  2385.     move.l    #$ff000000,d0
  2386.     moveq    #0,d6
  2387.     move.w    TDU_CURRTRK(A_UNIT),d6
  2388.     swap    d6
  2389.     or.l    d6,d0
  2390.     move.l    d3,d6
  2391.     lsl.l    #8,d6
  2392.     or.l    d6,d0
  2393.     or.l    d1,d0
  2394.     bsr    EncodeLong    ;header
  2395.  
  2396. ;Encode sector label
  2397.     push    a2
  2398.     lea    SectorLabels(A_DEVICE),a2
  2399.     move.l    d3,d0
  2400.     lsl.l    #4,d0    ;sector*16
  2401.     lea    (a2,d0.w),a2
  2402.     bsr    EncodeSectorLabels
  2403.     pop    a2
  2404.  
  2405.     move.l    d5,d0
  2406.     bsr    EncodeLong    ;header checksum
  2407.     move.l    a0,d2    ;save raw data pointer
  2408.     addq.l    #8,a0
  2409.     bsr    EncodeBlock    ;encode data block
  2410.     move.l    d5,d0
  2411.     exg    a0,d2
  2412.     bsr    EncodeLong    ;data block checksum
  2413.     bsr    Correct
  2414.     move.l    d2,a0
  2415.     addq.l    #1,d3
  2416.     subq.l    #1,d1
  2417.     bne    .SecLoop
  2418.  
  2419.     move.w    #$aaa8,(a0)
  2420.     bsr    Correct    ;extra word to avoid hardware bug
  2421.  
  2422. ;Physically write the data
  2423. .WriteAgain:
  2424.     bsr    SetRegs
  2425.     bsr    PreComp
  2426.     move.w    #ADKF_WORDSYNC,adkcon(a6)    ;turn OFF wordsync!!!
  2427.     move.w    d7,dsksync(a6)
  2428.     bsr    ClearSigs
  2429.  
  2430.     btst    #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
  2431.     beq    .SkipVerify
  2432.     bset    #DEVB_Verify,DEV_FLAGS(A_DEVICE)
  2433. .SkipVerify:
  2434.  
  2435.     move.l    VerifyBuffer(A_DEVICE),a0
  2436.     clr.l    (a0)+
  2437.     clr.l    (a0)
  2438.     move.w    #0,dsklen(a6)
  2439.     move.w    #$DA9E,dsklen(a6)
  2440.     move.w    #$DA9E,dsklen(a6)    ;write approx. 13,628 bytes
  2441.  
  2442. ;This piece of code (commented out) tests the function of the rare
  2443. ;"interrupt delayed" requester. (I've never seen it appear in actual use).
  2444.     comment |
  2445. ;TEST
  2446.     push    a6
  2447.     move.l    4,a6
  2448.     SYS    Disable
  2449.     move.w    #5000,d0
  2450. .L1:    move.b    vhposr+_custom,d1
  2451. .L2:    cmp.b    vhposr+_custom,d1
  2452.     beq    .L2
  2453.     dbra    d0,.L1
  2454.     SYS    Enable
  2455.     pop    a6
  2456. |
  2457.  
  2458.     btst    #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
  2459.     beq    .NoVerify
  2460.  
  2461. ;VERIFY
  2462.  
  2463. ;We verify by comparing the raw MFM data in RawBuffer (what we just wrote)
  2464. ;and VerifyBuffer (what is coming in). Due to the ingenious method of
  2465. ;verifying (thanks to Sebastiano Vigna!) the data comes in sector-by-sector
  2466. ;in the same order that we wrote it.
  2467.  
  2468.     move.l    VerifyBuffer(A_DEVICE),a0
  2469.     move.l    RawBuffer(A_DEVICE),a2
  2470.     lea    1666(a2),a2    ;first sector (minus sync)
  2471.     bsr    WaitWordSync
  2472.     tst.l    d7
  2473.     bne    .VerifyError
  2474.     bsr    WaitWordSync
  2475.     tst.l    d7
  2476.     bne    .VerifyError
  2477. ;Now we have our first sector in the verify buffer. Scan for a
  2478. ;sync mark. (There may be 1 or 2 sync marks).
  2479.     cmp.w    #$4489,(a0)+
  2480.     bne    .VerifyError
  2481.     cmp.w    #$4489,(a0)
  2482.     bne    .HaveSync
  2483.     addq.l    #2,a0
  2484. .HaveSync:
  2485.  
  2486. ;We go through a rather elaborate procedure here to make sure that we've
  2487. ;started reading with sector 0. (If not, display a requester informing the
  2488. ;user that something is locking out level-1 interrupts for a long period of
  2489. ;time).
  2490.     push    a2
  2491.     move.l    #$55555555,d2
  2492.     move.l    a0,a2
  2493.     bsr    DecodeLong
  2494.     pop    a2
  2495.     and.w    #$ff00,d0
  2496.     beq    .Sector0
  2497.     move.l    a0,a1
  2498.     moveq    #9,d0
  2499.     moveq    #0,d1
  2500. ..    move.l    (a1)+,d3
  2501.     eor.l    d3,d1
  2502.     dbra    d0,..
  2503.     and.l    d2,d1
  2504.     push    a2
  2505.     move.l    a1,a2
  2506.     bsr    DecodeLong
  2507.     pop    a2
  2508.     cmp.l    d0,d1
  2509.     bne    .VerifyError
  2510.  
  2511. ;Display an informational requester.
  2512.     bsr    StopDMA
  2513.     push    a6
  2514.     move.l    IntBase(A_DEVICE),a6
  2515.     cmp.w    #36,LIB_VERSION(a6)
  2516.     bhi    .KS20
  2517.  
  2518. ;Running under 1.3. Put up a DisplayAlert.
  2519.     moveq    #0,d0    ;alert type (recoverable)
  2520.     lea    .AlertLockOut(pc),a0
  2521.     moveq    #20,d1    ;height
  2522.     SYS    DisplayAlert
  2523.     pop    a6
  2524.     bra    .WriteAgain
  2525.  
  2526. ;Running under 2.0. Put up a EasyRequest.
  2527. .KS20:
  2528.     sub.l    a0,a0
  2529.     sub.l    a2,a2
  2530.     lea    .LockOut(pc),a1
  2531.     SYS    EasyRequestArgs
  2532.     pop    a6
  2533.     bra    .WriteAgain
  2534.  
  2535. ;Note: Probably should compare with the blitter, but this will be fairly
  2536. ;fast.
  2537.  
  2538. .Sector0:
  2539.     move.w    #270-1,d0
  2540. ..    cmp.l    (a0)+,(a2)+
  2541.     dbne    d0,..
  2542.     bne    .VerifyError
  2543.  
  2544. ;Now we are over the initial hump of the first sync mark. The rest of the
  2545. ;compare is even easier.
  2546.     moveq    #9-1,d1
  2547. .VLoop:    bsr    WaitWordSync
  2548.     tst.l    d7
  2549.     bne    .VerifyError
  2550.     move.w    #272-1,d0
  2551. ..    cmp.l    (a0)+,(a2)+
  2552.     dbne    d0,..
  2553.     bne    .VerifyError
  2554.     dbra    d1,.VLoop
  2555.  
  2556. ;We have one more sector to verify. This time we must wait for "Block
  2557. ;done", rather than another sync.
  2558.  
  2559.     bsr    DISK_Wait
  2560.     tst.l    d7
  2561.     bne    .VerifyError
  2562.     move.w    #272-1,d0
  2563. ..    cmp.l    (a0)+,(a2)+
  2564.     dbne    d0,..
  2565.     bne    .VerifyError
  2566.     bra    .End
  2567.  
  2568. .VerifyError:
  2569.     moveq    #0,d7    ;don't propagate the error to the app
  2570. ;We go here if an error is detected during the verify. We first shut down
  2571. ;the read operation that may be in progress, then put up a requester and let
  2572. ;the user choose whether to retry or abort.
  2573.  
  2574.     bsr    StopDMA    ;stop!!
  2575.     push    a6
  2576.     move.l    IntBase(A_DEVICE),a6
  2577.     cmp.w    #36,LIB_VERSION(a6)
  2578.     bhi    .DoKS20
  2579.  
  2580. ;Running under 1.3. Put up a DisplayAlert.
  2581.     moveq    #0,d0    ;alert type (recoverable)
  2582.     lea    .AlertVError(pc),a0
  2583.     moveq    #20,d1    ;height
  2584.     SYS    DisplayAlert
  2585. ;D0 is set to 'TRUE' if the LEFT button was pressed.
  2586.     pop    a6
  2587.     tst.l    d0
  2588.     bne    .WriteAgain    ;go if left button pressed
  2589.     bra    .End
  2590.  
  2591. ;Running under 2.0. Put up a EasyRequest.
  2592. .DoKS20:    sub.l    a0,a0
  2593.     sub.l    a2,a2
  2594.     lea    .VError(pc),a1
  2595.     SYS    EasyRequestArgs
  2596.     pop    a6
  2597.     tst.l    d0
  2598.     bne    .WriteAgain    ;go if left gadget hit
  2599.     bra    .End
  2600. .NoVerify:
  2601.     bsr    DISK_Wait
  2602.     move.l    #3*1000,d0
  2603.     bsr    delay    ;post-write delay
  2604. .End:    movem.l    (sp)+,d0-d6/a0-a2
  2605.     rts
  2606. .Protected:
  2607.     moveq    #DISK_WriteProtected,d7
  2608.     bra    .End
  2609.  
  2610. .AlertVError:
  2611.     dc.w    10    ;x coordinate
  2612.     dc.b    10    ;y coordinate
  2613.     dc.b    '*** VERIFY ERROR !!! ***  Hit LEFT button to RETRY'
  2614.     dc.b    ', or RIGHT button to CANCEL.',0
  2615.     dc.b    0    ;continuation byte
  2616.     even
  2617.  
  2618. .AlertLockOut:
  2619.     dc.w    10    ;x coordinate
  2620.     dc.b    10    ;y coordinate
  2621.     dc.b    'Disk block interrupt delayed by >10ms.'
  2622.     dc.b    '  Press mouse button.',0
  2623.     dc.b    0    ;continuation byte
  2624.     even
  2625.  
  2626. .VError:    dc.l    es_SIZEOF
  2627.     dc.l    0
  2628.     dc.l    .Title
  2629.     dc.l    .MainText
  2630.     dc.l    .GadgetText
  2631. .LockOut:    dc.l    es_SIZEOF
  2632.     dc.l    0
  2633.     dc.l    .Title
  2634.     dc.l    .LockOutTxt
  2635.     dc.l    .Okay
  2636. .LockOutTxt:
  2637.     dc.b    'Disk block interrupt delayed by >10ms caused erroneous'
  2638.     dc.b    ' verify.',0
  2639. .Okay:    dc.b    'If you say so. Try it again!',0
  2640. .Title:    dc.b    'hackdisk.device message',0
  2641. .MainText:
  2642.     dc.b    '*** VERIFY ERROR!!! ***',0
  2643. .GadgetText:
  2644.     dc.b    'Retry|Cancel',0
  2645.     even
  2646.  
  2647. DISK_Write:
  2648.  
  2649. ;Error code returned in D7, as always.
  2650.  
  2651.     movem.l    d0-d5/a0-a2,-(sp)
  2652.  
  2653.     btst    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  2654.     bne    .ProtError
  2655.  
  2656.     move.l    d0,d5    ;length
  2657.  
  2658. ;The meat of the write routine...
  2659.  
  2660. .WriteLoop:
  2661.     move.l    d1,d3    ;offset
  2662.     divu.w    d6,d3    ;d3.w is track #
  2663.     move.l    d3,d4
  2664.     clr.w    d4
  2665.     swap    d4    ;d4.l is byte offset into track
  2666.     bsr    MinSeek
  2667.     tst.l    d4    ;any offset?
  2668.     bne    .Complex    ;yes
  2669.     cmp.l    d6,d5    ;at least a track left?
  2670.     blo    .Complex    ;no
  2671.     move.l    a0,d0
  2672.     btst    #0,d0    ;word aligned?
  2673.     bne    .Complex    ;no, do it the long way
  2674.     move.l    a0,a2
  2675.     bsr    EncodeAndWriteTrack
  2676.     tst.l    d7
  2677.     bne    .End
  2678.     sub.l    d6,d5    ;update length
  2679.     beq    .End
  2680.     add.l    d6,a0    ;update source pointer
  2681.     add.l    d6,d1    ;update offset
  2682.     bra    .WriteLoop
  2683.  
  2684. .Complex:
  2685.  
  2686. ;This part is somewhat difficult. We check the offset and length parameters
  2687. ;to see whether they're a multiple of 512. If so, we keep track of which
  2688. ;sectors will be written in the buffer. This information is later used by
  2689. ;Update to determine whether a part of the original track must be read in.
  2690. ;(We don't attempt this optimization if the user is writing some odd number
  2691. ;of bytes...This is probably why trackdisk has the limits that it does).
  2692.  
  2693.     tst.l    d5
  2694.     beq    .End    ;nothing left, forget it
  2695.     move.l    d4,d0
  2696.     and.w    #%111111111,d0
  2697.     bne    .NoOpt
  2698.     move.l    d4,d0
  2699.     move.l    d5,d2
  2700.     and.w    #%111111111,d2
  2701.     bne    .NoOpt
  2702.     move.l    d5,d2
  2703.     lsr.l    #8,d0
  2704.     lsr.l    #1,d0    ;get starting sector number
  2705.     lsr.l    #8,d2
  2706.     lsr.l    #1,d2    ;get length in sectors
  2707.     move.l    WriteMap(A_DEVICE),d7
  2708. .OptLoop:    bset    d0,d7
  2709.     addq.b    #1,d0
  2710.     cmp.b    #32,d0
  2711.     beq    .EOpt
  2712.     subq.l    #1,d2
  2713.     bne    .OptLoop
  2714. .EOpt:    move.l    d7,WriteMap(A_DEVICE)
  2715.     moveq    #0,d7
  2716.  
  2717. ;This is normally done by ReadTrackAndDecode.
  2718.     move.b    UnitNum(A_UNIT),BufferDrive(A_DEVICE)
  2719.     move.b    TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
  2720.     bra    .Opt    ;don't read (yet)
  2721.  
  2722. .NoOpt:    push    a0
  2723.     move.l    DecodedBuffer(A_DEVICE),a0
  2724.     bsr    ReadTrackAndDecodeBuffered
  2725.     pop    a0
  2726.     tst.l    d7
  2727.     bne    .End
  2728. .Opt:    move.l    DecodedBuffer(A_DEVICE),a1
  2729.     add.l    d4,a1    ;add byte offset into track
  2730.     sub.l    d6,d4
  2731.     neg.l    d4
  2732.     add.l    d4,d1    ;update offset
  2733.  
  2734. ;D4.L - number of bytes that could be transferred from this track
  2735. ;D5.L - number of bytes left in the entire Read request
  2736.  
  2737.     cmp.l    d5,d4
  2738.     bhs    .FinalWrite
  2739.     sub.l    d4,d5    ;update length
  2740.     move.l    d4,d0
  2741.     bsr    CopyMem
  2742.     add.l    d0,a0    ;update dest pointer
  2743.     bset    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  2744.     bra    .WriteLoop
  2745.  
  2746. .FinalWrite:
  2747.     move.l    d5,d0
  2748.     bsr    CopyMem
  2749.     bset    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  2750. .End:    movem.l    (sp)+,d0-d5/a0-a2
  2751.     rts
  2752. .ProtError:
  2753.     moveq    #DISK_WriteProtected,d7
  2754.     bra    .End
  2755.  
  2756. MotorOff:
  2757. ;MotorOff turns off all drive motors and leaves all drives deselected
  2758.     bsr    Deselect
  2759.     bset    #CIAB_DSKMOTOR,ciabprb    ;motor off
  2760.     and.b    #~(CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3),ciabprb    ;select all drives
  2761.     bsr    Deselect
  2762.     clr.b    MotorState(A_DEVICE)
  2763.     rts
  2764.  
  2765. Inquire:
  2766.     IFND    _LVOGetUnitID
  2767. _LVOGetUnitID    equ    -30
  2768.     ENDC
  2769.     movem.l    d0-d2/a0-a1/a6,-(sp)
  2770.     moveq    #0,d2
  2771.     move.l    DiskResourceBase(A_DEVICE),a6
  2772. .Loop:    move.l    d2,d0
  2773.     SYS    GetUnitID
  2774.     tst.l    d0
  2775.     bne    .Next
  2776.     bset    d2,InquireBits(A_DEVICE)
  2777. .Next:    addq.l    #1,d2
  2778.     cmp.w    #MD_NUMUNITS,d2
  2779.     blo    .Loop
  2780.     movem.l    (sp)+,d0-d2/a0-a1/a6
  2781.     rts
  2782.  
  2783.     comment |
  2784. ;Find out what drives are in the system
  2785. ;Exit with "drive map" in D0 (bit 0 = drive 0, bit 1 = drive 1, etc.)
  2786.  
  2787. ;See the Hardware Reference Manual, Appendix E for a description of
  2788. ;the procedure.
  2789.  
  2790. ;Note: Drive zero is always present. In fact, the identification 
  2791. ;scheme does not work with drive zero (except for the half-speed drive).
  2792.  
  2793. Inquire:
  2794.     movem.l    d1-d5/a0,-(sp)
  2795.     moveq    #4,d3
  2796.     moveq    #1,d2    ;drive count
  2797.     moveq    #1,d0    ;drive map (drive zero always present)
  2798.     moveq    #7,d1
  2799.     move.l    #ciabprb,a0
  2800.  
  2801.     bsr    Deselect
  2802.  
  2803. .InquireLoop:
  2804.     moveq    #31,d4
  2805.     moveq    #0,d5    ;identification longword
  2806.  
  2807.     bclr    d1,(a0)    ;motor on
  2808.     bset    d3,(a0)    ;deselect drive
  2809.     bclr    d3,(a0)    ;select drive
  2810.  
  2811.     bset    d1,(a0)    ;motor off
  2812.     bset    d3,(a0)    ;deselect drive
  2813.     bclr    d3,(a0)    ;select drive
  2814.     bset    d3,(a0)    ;deselect drive
  2815.  
  2816. .ReadIdent:
  2817.     bclr    d3,(a0)    ;select drive
  2818.     btst    #5,ciaapra    ;test ready
  2819.     beq    .zero
  2820.     bset    d4,d5
  2821. .zero:    bset    d3,(a0)    ;deselect drive
  2822.     dbra    d4,.ReadIdent
  2823.  
  2824.     tst.l    d5
  2825.     bne    .Skip
  2826.     bset    d2,d0
  2827. .Skip:    addq.l    #1,d3
  2828.     addq.l    #1,d2
  2829.     cmp.b    #4,d2
  2830.     bne    .InquireLoop
  2831.     movem.l    (sp)+,d1-d5/a0
  2832.     rts
  2833. |
  2834.  
  2835. SeekZeroAll:
  2836.     movem.l    d0-d2/A_UNIT,-(sp)
  2837.     move.b    InquireBits(A_DEVICE),d0
  2838.     lea    Unit0(A_DEVICE),A_UNIT
  2839.     moveq    #0,d1
  2840.     moveq    #MD_NUMUNITS-1,d2
  2841. .Loop:    btst    d1,d0
  2842.     beq    .Next
  2843.     bsr    SeekZero
  2844. .Next:    lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  2845.     addq.b    #1,d1
  2846.     dbra    d2,.Loop
  2847.     movem.l    (sp)+,d0-d2/A_UNIT
  2848.     rts
  2849.  
  2850. SeekZero:
  2851. ;Places the drive in UnitNum(A_UNIT) on track zero.
  2852. ;Drive does not need to be selected in advance.
  2853. ;Drive will be left _deselected_!
  2854.  
  2855.     movem.l    d0-d1,-(sp)
  2856.  
  2857. .StepLoop:
  2858.     bsr    SelectDriveSameMotor
  2859.     bset    #CIAB_DSKDIREC,ciabprb    ;set to "out" (lower tracks)
  2860.     btst    #CIAB_DSKTRACK0,ciaapra    ;check track zero flag
  2861.     beq    .EndStepLoop
  2862.     bset    #CIAB_DSKSTEP,ciabprb
  2863.     bclr    #CIAB_DSKSTEP,ciabprb    ;step head
  2864.     bset    #CIAB_DSKSTEP,ciabprb
  2865.     bsr    Deselect
  2866.     move.l    TDU_CALIBRATEDELAY(A_UNIT),d0
  2867.     bsr    delay
  2868.     bra    .StepLoop
  2869.  
  2870. .EndStepLoop:
  2871.     bsr    Deselect
  2872.     move.l    TDU_SETTLEDELAY(A_UNIT),d0
  2873.     bsr    delay
  2874.     clr.w    TDU_CURRTRK(A_UNIT)
  2875.     movem.l    (sp)+,d0-d1
  2876.     rts
  2877.  
  2878. CopyMemSlow:
  2879. ;Only to be called by CopyMem
  2880.     move.l    4,a6
  2881.     SYS    CopyMem
  2882.     movem.l    (sp)+,d0-d7/a0-a6
  2883.     rts
  2884.  
  2885. CopyMem:
  2886.  
  2887. ;A0 - source
  2888. ;A1 - destination
  2889. ;D0 - size
  2890.  
  2891.     movem.l    d0-d7/a0-a6,-(sp)
  2892.  
  2893.     move.l    a0,d1
  2894.     btst    #0,d1
  2895.     bne    CopyMemSlow
  2896.     move.l    a1,d1
  2897.     btst    #0,d1
  2898.     bne    CopyMemSlow
  2899.  
  2900.  
  2901. .More:    cmp.l    #512,d0
  2902.     blo    CopyMemSlow
  2903.  
  2904. ;Copy 480 bytes
  2905. n    set    0
  2906.     REPT    10
  2907.     movem.l    (a0)+,d1-d7/a2-a6
  2908.     movem.l    d1-d7/a2-a6,n*48(a1)
  2909. n    set    n+1
  2910.     ENDR
  2911. ;Copy 32 bytes
  2912.     movem.l    (a0)+,d1-d7/a2
  2913.     movem.l    d1-d7/a2,480(a1)
  2914.     lea    512(a1),a1
  2915.     sub.l    #512,d0
  2916.     bne    .More
  2917.     movem.l    (sp)+,d0-d7/a0-a6
  2918.     rts
  2919.  
  2920. ;Debugging stuff
  2921.  
  2922.     ifne    INFO_LEVEL    ;If any debugging enabled at all
  2923. KPutFmt:    move.l    a2,-(sp)
  2924.     lea    KPutChar(pc),a2
  2925.     bsr    KDoFmt
  2926.     move.l    (sp)+,a2
  2927.     rts
  2928.  
  2929. KDoFmt:    move.l    a6,-(sp)
  2930.     move.l    4,a6
  2931.     SYS    RawDoFmt
  2932.     move.l    (sp)+,a6
  2933.     rts
  2934.  
  2935. KPutChar:
  2936.  
  2937. ;Serial
  2938.     comment |
  2939.     move.l    a6,-(sp)
  2940.     move.l    4,a6
  2941.     SYS    RawPutChar
  2942.     move.l    (sp)+,a6
  2943.     rts
  2944. |
  2945.  
  2946. ;Printer
  2947.     comment |
  2948.     move.b    #$ff,$bfe301
  2949. .Print:    btst    #0,$bfd000
  2950.     bne    .Print
  2951.     move.b    d0,$bfe101
  2952.     rts
  2953. |
  2954.  
  2955. ;Memory
  2956.     tst.l    MemPtr
  2957.     bne    .OK
  2958.     move.l    #$500000,MemPtr
  2959. .OK:
  2960.     push    a0
  2961.     move.l    MemPtr(pc),a0
  2962.     move.b    d0,(a0)+
  2963.     move.l    a0,MemPtr
  2964.     pop    a0
  2965.     rts
  2966.  
  2967. MemPtr:    dc.l    0
  2968.  
  2969.     endc
  2970.  
  2971. EndCode:
  2972.